Authentication講解(Spring security認證)


標准認證過程:

1.用戶使用username和password登錄

2.系統驗證這個password對於該username是正確的

3.假設第二步驗證成功,獲取該用戶的上下文信息(如他的角色列表)

4.圍繞該用戶建立安全上下文(security context)

5.用戶可能繼續進行的一些操作被一個驗證控制機制潛在的管理,這個驗證機制會根據當前用戶的安全上下文來驗證權限。

 

認證過程就是又前三項構成的。在Spring Security中是這樣處理這三部分的:

1.username和password被獲得后封裝到一個UsernamePasswordAuthenticationToken(Authentication接口的實例)的實例中

2.這個token被傳遞給AuthenticationManager進行驗證

3.成功認證后AuthenticationManager將返回一個得到完整填充的Authentication實例

4.通過調用SecurityContextHolder.getContext().setAuthentication(...),參數傳遞authentication對象,來建立安全上下文(security context)

 

可以從一個示例代碼中觀察整個過程(完整代碼參考Spring-Security文檔9.3.1節):

 1 public class AuthenticationExample {
 2     
 3     private static AuthenticationManager am = new SampleAuthenticationManager();
 4 
 5     public static void main(String[] args) throws Exception {
 6         
 7         String name = "";
 8         String password = "";
 9         try {
10             // request就是第一步,使用name和password封裝成為的token
11             Authentication request = new UsernamePasswordAuthenticationToken(name, password);
12             // 將token傳遞給Authentication進行驗證
13             Authentication result = am.authenticate(request);
14             SecurityContextHolder.getContext().setAuthentication(result);
15             break;
16         } catch (AuthenticationException e) {
17             System.out.println("認證失敗:" + e.getMessage());
18         }
19         System.out.println("認證成功,Security context 包含:" + SecurityContextHolder.getContext().getAuthentication());
20     }
21 }
22 
23 // 自定義驗證方法
24 class SimpleAuthenticationManager implements AuthenticationManager {
25     static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
26 
27     // 構建一個角色列表
28     static {
29         AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
30     }
31 
32     // 驗證方法
33     public Authentication authenticate(Authentication auth) throws AuthenticationException {
34         // 這里我們自定義了驗證通過條件:username與password相同就可以通過認證
35         if (auth.getName().equals(auth.getCredentials())) {
36             return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), AUTHORITIES);
37         }
38         // 沒有通過認證則拋出密碼錯誤異常
39         throw new BadCredentialsException("Bad Credentials");
40     }
41 }

 

Web Application

考慮一個典型的Web應用認證過程:

1.訪問首頁,隨便點擊一個鏈接

2.發送一個請求到服務器,服務器判斷你是否在訪問一個收到保護的資源

3.此時你還沒有進行認證,服務器會返回一個響應告訴你必須先通過認證。這個響應可以是一個HTTP響應碼或者是重定向到指定的web頁面

4.根據認證機制,你的瀏覽器可能會重定向到一個登錄頁面,或者通過某種方式恢復你的身份(通過一個基礎的認證對話框,cookie,X.509證明等)

5.瀏覽器回應服務器。這可以是一個HTTP POST請求,包含你所填寫的表單信息,也可以是一個HTTP請求頭,包含你的認證詳情

6.接下來服務器會判定提交的憑證是否通過認證。如果認證通過,那么繼續下一步。如果沒有通過認證,那么重新進行上邊的步驟

7.你在認證之前,原始的請求(即觸發認證的請求)將會重新發起。

 

Spring Security已經實現了上述的大多數過程。主要有ExceptionTranslationFilter,AuthenticationEntryPoint和一個認證機制,負責調用上面討論過的AuthenticationManager。

ExceptionTranslationFilter:

ExceptionTranslationFilter是用來檢測Spring Security拋出的任何異常的過濾器。

 

AuthenticationProvider和UserDetails:

There is often some confusion about UserDetailsService. It is purely a DAO for user data and performs no other function other than to supply that data to other components within the framework. In particular, it does not authenticate the user, which is done by the AuthenticationManager. In many cases it makes more sense to implement AuthenticationProvider directly if you require a custom authentication process.

UserDetailsService經常有些困惑。它純粹是用戶數據的一個DAO,除了將該數據提供給框架內的其他組件之外,沒有其他功能。特別是,它不驗證用戶,這是由身份驗證管理器完成的。在許多情況下,如果需要自定義身份驗證過程,直接執行身份驗證提供程序就更有意義。

 

AuthenticationManager:

用來處理一個認證請求。只有一個authentication(Authentication authentication)函數。

嘗試去認證傳入的Authentication對象,如果認證成功,返回一個完整填充的Authentication對象(包括授予的權限)。

一個AuthenticationManager必須處理以下異常:

  • DisabledException:當一個賬戶被禁用且AuthenticationManager可以檢測出來這個狀態,要拋出該異常
  • LockedException:當一個賬戶被鎖且AuthenticationManager可以檢測這個狀態,要拋出該異常
  • BadCredentialsException:當賬戶認證失敗,必須拋出該異常。(一個AuthenticationManager必須檢測這個狀態)

這些異常應該按照順序拋出,(比如如果一個賬戶被鎖定,那么不進行賬戶認證)。

 

AuthenticationProvider:

用來處理一個指定的認證。有一個authenticate(Authentication authentication)函數和一個supports(Class<?> authentication)函數。

其中authenticate函數的用法與AuthenticationManager的authenticate一樣。

supports函數用來指明該Provider是否適用於該類型的認證,如果不合適,則尋找另一個Provider進行驗證處理。

 

ProviderManager:

通過AuthenticationProviders迭代認證請求。

AuthenticationProviders通常按照順序嘗試,直到返回一個不為null的響應。非空響應代表provider可以提供認證並且不會繼續請求下一個provider。如果后邊的provider成功進行了驗證,那么前邊provider拋出的異常將被忽略。

 

CustomUserDetail代碼(java)

public class CustomUserDetail implements UserDetails {

    private User user;
    private List<SimpleGrantedAuthority> authorities = new ArrayList<>();

    public CustomUserDetail() {}

    public CustomUserDetail(User user, List<Role> roles) {
        this.user = user;
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getCode()));
            System.out.println("Role:" + role.getCode());
        }
    }

    public String getName() {
        return user.getName();
    }

    public User getUser() {
        return user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getMobile();
    }

    @Override
    public boolean isAccountNonExpired() {
        return user.getEnabled();
    }

    @Override
    public boolean isAccountNonLocked() {
        return user.getEnabled();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return user.getEnabled();
    }

    @Override
    public boolean isEnabled() {
        return user.getEnabled();
    }
}

 

CustomUserDetailService代碼(java)

public class CustomUserDetailService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;

    @Autowired
    RoleMapper roleMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        User user = userMapper.getUserByMobile(s);
        if (user != null) {
            List<Role> roles = roleMapper.getRolesByUserId(user.getId());
            return new CustomUserDetail(user, roles);
        } else {
            throw new UsernameNotFoundException("用戶不存在");
        }
    }
}

CustomAuthenticationProvider代碼(java)

public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private CustomUserDetailService userDetailService;

    @Override//Authentication中封裝了username和password
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String)authentication.getCredentials();//Credential證書

        UserDetails userDetails = userDetailService.loadUserByUsername(username);
        String md5Pwd = DigestUtils.md5DigestAsHex(password.getBytes());

        if (userDetails.getPassword().equals(md5Pwd)) {
            return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        } else {
            throw new BadCredentialsException("密碼錯誤");
        }
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

 

 

 本博客引用自:https://www.cnblogs.com/shiyu404/p/6530894.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM