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的默認登錄地址為

