UsernamePasswordAuthenticationFilter介绍
UsernamePasswordAuthenticationFilter是AbstractAuthenticationProcessingFilter针对使用用户名和密码进行身份验证而定制化的一个过滤器。其添加是在调用http.formLogin()时作用,默认的登录请求pattern为"/login",并且为POST请求。当我们登录的时候,也就是匹配到loginProcessingUrl,这个过滤器就会委托认证管理器authenticationManager来验证登录。
自定义UsernamePasswordAuthenticationFilter
这里我的需求是通过自定义UsernamePasswordAuthenticationFilter实现对前端传过来的密码进行RSA私钥解密,并且因为登录地址不是"/login",所以继承的是AbstractAuthenticationProcessingFilter,如果登录地址为默认,那么可直接继承UsernamePasswordAuthenticationFilter重写attemptAuthentication方法即可。
public class MyUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY; private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY; private boolean postOnly = true; private String privateKey = "xxxxxxxxxxxxxxxxxxx"; public MyUsernamePasswordAuthenticationFilter() { super(new AntPathRequestMatcher("/oauth/token", "POST")); } public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); try { password = RSAUtil.decrypt(password, privateKey); } catch (Exception e) { e.printStackTrace(); } if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return super.getAuthenticationManager().authenticate(authRequest); } public void setAuthenticationManager(AuthenticationManager authenticationManager) { super.setAuthenticationManager(authenticationManager); } protected String obtainPassword(HttpServletRequest request) { return request.getParameter(passwordParameter).replaceAll(" ", "+"); } protected String obtainUsername(HttpServletRequest request) { return request.getParameter(usernameParameter); } protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } public void setUsernameParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.usernameParameter = usernameParameter; } public void setPasswordParameter(String passwordParameter) { Assert.hasText(passwordParameter, "Password parameter must not be empty or null"); this.passwordParameter = passwordParameter; } public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getUsernameParameter() { return usernameParameter; } public final String getPasswordParameter() { return passwordParameter; } }
把自定义 MyUsernamePasswordAuthenticationFilter 添加到 Filter Chain 过滤器链中
在 SpringSecurity 的配置类中使用 http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) 把自定义的过滤器放在UsernamePasswordAuthenticationFilter 的位置,并为其设置认证成功和失败的处理方法以及认证管理器AuthenticationManager
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthenticationSuccessHandler appLoginInSuccessHandler; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { AuthenticationManager manager = super.authenticationManagerBean(); return manager; } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and().httpBasic().and() .cors().disable().headers().frameOptions().sameOrigin();// 解决iframe无法访问 http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean MyUsernamePasswordAuthenticationFilter myAuthenticationFilter() throws Exception { MyUsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); filter.setAuthenticationSuccessHandler(appLoginInSuccessHandler); filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); response.getWriter().write(JSON.toJSONString(Respon.failed("登录失败!"))); } }); return filter; } }
自定义UsernamePasswordAuthenticationFilter的运用
除了上面的例子,还常用于修改表单登录变为使用Json格式登录,登录验证码等等,需要注意的地方是UsernamePasswordAuthenticationFilter的默认登录地址为