Authentication講解(Spring security認證)

標準認證過程:

1.用戶使用username和password登陸html

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

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

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

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

 

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

1.username和password被得到後封裝到一個UsernamePasswordAuthenticationToken(Authentication接口的實例)的實例中服務器

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

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

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

相關文章
相關標籤/搜索