需要先增加一个自定义的Filter去继承 UsernamePasswordAuthenticationFilter 或者 AbstractAuthenticationProcessingFilter
然后在自定义的Filter里面指定登录的Url . 设置过滤器的时候,必须为过滤器指定一个 authenticationManager ,并且初始化构造函数的时候,要传入该manager.
再编写一个Provider , 把自定义的UserDetailService传给该provider.
具体实现过程如下:
添加配置:
@Override protected void configure(AuthenticationManagerBuilder auth) { //增加自定义的UserDetailService userDetailsAuthenticationProvider.setUserDetailsService(userDetailsService); //设置一个Provider auth.authenticationProvider(userDetailsAuthenticationProvider); }
关键配置:
@Override protected void configure(HttpSecurity http) throws Exception { //手动实现的权限验证管理器 movieAuthorizeConfigProviderManager.configure(http.authorizeRequests()); //添加前台登录验证过滤器与userDetailService,不同的登录处理URL需要在Filter里面指定 UserAuthenticationFilter userAuthenticationFilter = new UserAuthenticationFilter(); //每个Filter必须指定一个authenticationManager userAuthenticationFilter.setAuthenticationManager(authenticationManager()); //设置登录成功处理事件 userAuthenticationFilter.setAuthenticationSuccessHandler(movieAuthenticationSuccessHandler); //设置登录失败处理事件 userAuthenticationFilter.setAuthenticationFailureHandler(movieAuthenticationFailureHandler); http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
完整配置:
自定义过滤器:
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public static final String POST = "POST"; public MyAuthenticationFilter() { this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login/check", "POST")); this.setAuthenticationManager(getAuthenticationManager()); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!request.getMethod().equals(POST)) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); //主要通过这个token来决定使用哪个userDetailService.
//UserDetailsAuthenticationProvider里面有个supports方法,主要用来验证指定的token是否符合.
//可以通过指定不同类型的token来决定使用哪个userDetailService. UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } }
Provider:
@Component public class UserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { private volatile String userNotFoundEncodedPassword; private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; @Autowired private PasswordEncoder passwordEncoder; private UserDetailsService userDetailsService; @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { 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")); } } @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); try { UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username); if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { throw new InternalAuthenticationServiceException(ex.getMessage(), ex); } } private void prepareTimingAttackProtection() { if (this.userNotFoundEncodedPassword == null) { this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); } } private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); } } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } }
UserDetailService:
@Component @Slf4j @Qualifier("normalUserDetailService") public class UserDetailServiceImpl implements UserDetailsService { private final IUserDao userDao; public UserDetailServiceImpl(IUserDao userDao) { this.userDao = userDao; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { club.cearnach.movie.entity.User user = userDao.findByAccount(username) .orElseThrow(() -> new UsernameNotFoundException("找不到指定的用户")); List<GrantedAuthority> authorities = AuthorityUtils .commaSeparatedStringToAuthorityList( MovieSecurityConstants.ROLE_PREFIX.concat(user.getRole().getName())); return new User(user.getAccount(), user.getPassword(), authorities); } }
第二个UserDetailService的实现:
@Component @Qualifier("adminDetailService") public class AdminUserDetailServiceImpl implements UserDetailsService { private final IAdminService adminService; public AdminUserDetailServiceImpl(IAdminService adminService) { this.adminService = adminService; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Admin admin = adminService.findByAccount(username) .orElseThrow(() -> new UsernameNotFoundException(AdminException.ADMIN_CAN_NOT_FOUNT)); List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList( MovieSecurityConstants.ROLE_PREFIX.concat(admin.getRole().getName())); return new User(admin.getAccount(), admin.getPassword(), authorities); } }