SpringSecurity自定義AuthenticationProvider和AuthenticationFilter
AuthenticationProvider
- 預設實現:DaoAuthenticationProvider
授權方式提供者,判斷授權有效性,使用者有效性,在判斷使用者是否有效性,它依賴於UserDetailsService例項,開發人員可以自定義UserDetailsService的實現。
- additionalAuthenticationChecks方法校驗密碼有效性
- retrieveUser方法根據使用者名稱獲取使用者
@Component @Slf4j public class LindAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { @Autowired UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; /** * 校驗密碼有效性. * * @param userDetails. * @param authentication . * @throws AuthenticationException . */ @Override protected void additionalAuthenticationChecks( UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } /** * 獲取使用者. * * @param username. * @param authentication . * @return * @throws AuthenticationException . */ @Override protected UserDetails retrieveUser( String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { UserDetails loadedUser = userDetailsService.loadUserByUsername(username); if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } }
AuthenticationFilter
- 預設實現:UsernamePasswordAuthenticationFilter
授權過濾器,你可以自定義它,並把它新增到預設過濾器前或者後去執行,主要用來到授權的持久化,它可以從請求上下文中獲取你的user,password等資訊,然後去判斷它是否符合規則,最後通過authenticate方法去授權。預設的UsernamePasswordAuthenticationFilter
過濾器,主要判斷請求方式是否為post,並且對username和password進行了預設值的處理,總之,在這個過濾器裡不會涉及到具體業務。
public class LindUserNameAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public LindUserNameAuthenticationFilter() { super(new AntPathRequestMatcher("/login", "GET")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String username = request.getParameter("username"); String password = request.getParameter("password"); if (username == null) { throw new InternalAuthenticationServiceException("Failed to get the username"); } if (password == null) { throw new InternalAuthenticationServiceException("Failed to get the password"); } UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); return this.getAuthenticationManager().authenticate(authRequest); } }
UserDetialsService
這是一個介面,有預設的實現方式,一般的,我們需要根據業務去重新實現它,比如從你的使用者表獲取當前授權的使用者資訊,你需要在UserDetialsService實現類裡對使用者表進行讀取操作;它一般會在AuthenticationProvider裡的retrieveUser方法中被使用,這就像面向物件裡的模板方法模式一樣,springSecurity把檢驗的步驟設計好了,咱們開發只要根據規則去實現具體細節就好。
@Component public class MyUserDetailService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException { // mock程式碼,正常應該從資料庫讀取 User user = new User(name, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("read,ROLE_USER"));//設定許可權和角色 return user; } }
認證時執行的順序
- LindUserNameAuthenticationFilter
- LindAuthenticationProvider.retrieveUser
- LindAuthenticationProvider.additionalAuthenticationChecks
- UserDetailsService
- Authentication
springSecurity原始碼:https://github.com/spring-projects/spring-security