因爲要在登陸的時候增加企業編號驗證,自己定義了DisUsernamePasswordAuthenticationFilter類並繼承AbstractAuthenticationProcessingFilter這個類,但是運行後發現只要用戶名密碼錯誤或者沒有權限等其他錯誤都返回401 -Authentication Failed,如下圖:
後經過排查發現,在重寫AbstractAuthenticationProcessingFilter類時沒有傳入登錄失敗跳轉的路徑failureUrl.找到問題後我在AbstractAuthenticationProcessingFilter發現該類定義了AuthenticationSuccessHandler和AuthenticationFailureHandler,所以我們可以在application-security.xml中定義一下代碼:
<beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFai lureHandler"> <!-- 可以配置相應的跳轉方式。屬性forwardToDestination爲true採用forward false爲sen dRedirect --> <beans:property name="defaultFailureUrl" value="/login.jsp?auth-failure=true"></b eans:property> </beans:bean>
並在自定義的DisUsernamePasswordAuthenticationFilter類中引入
<!-- 登錄處理Filter --> <beans:bean id="loginProcessFilter" class="com.fpi.safety.common.security.DisUsername PasswordAuthenticationFilter"> <beans:property name="companyCode" value="companyCode" /> <beans:property name="usernameParameter" value="username" /> <beans:property name="passwordParameter" value="password" /> <!-- 登陸失敗處理類 --> <beans:property name="simpleUrlAuthenticationFailureHandler" ref="simpleUrlAuthen ticationFailureHandler" /> <beans:property name="authenticationSuccessHandler" ref="appSessionSuccessHandler " /> <beans:property name="authenticationManager" ref="disuserAuthManager" /> </beans:bean>
DisUsernamePasswordAuthenticationFilter.java
public class DisUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password"; public static final String SPRING_SECURITY_FORM_COMPANY_CODE_KEY = "company_code"; public static final String USERNAME_LOGINID_SPLIT = "-"; /** * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler} */ @Deprecated public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME"; private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY; private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY; private String companyCode = SPRING_SECURITY_FORM_COMPANY_CODE_KEY; private SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler; private boolean postOnly = true; //~ Constructors =================================================================================================== public DisUsernamePasswordAuthenticationFilter() { super("/j_spring_security_check"); } //~ Methods ======================================================================================================== 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); String companyCode = obtainCompanyCode(request); if (username == null) { username = ""; } if (password == null) { password = ""; } if(companyCode == null || companyCode.equals("")){ companyCode = "null"; } username = username.trim(); companyCode = companyCode.trim(); username = username+USERNAME_LOGINID_SPLIT+companyCode; UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } protected String obtainCompanyCode(HttpServletRequest request) { return request.getParameter(companyCode); } /** * Enables subclasses to override the composition of the password, such as by including additional values * and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the * password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The * <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p> * * @param request so that request attributes can be retrieved * * @return the password that will be presented in the <code>Authentication</code> request token to the * <code>AuthenticationManager</code> */ protected String obtainPassword(HttpServletRequest request) { return request.getParameter(passwordParameter); } /** * Enables subclasses to override the composition of the username, such as by including additional values * and a separator. * * @param request so that request attributes can be retrieved * * @return the username that will be presented in the <code>Authentication</code> request token to the * <code>AuthenticationManager</code> */ protected String obtainUsername(HttpServletRequest request) { return request.getParameter(usernameParameter); } /** * Provided so that subclasses may configure what is put into the authentication request's details * property. * * @param request that an authentication request is being created for * @param authRequest the authentication request object that should have its details set */ protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } /** * Sets the parameter name which will be used to obtain the username from the login request. * * @param usernameParameter the parameter name. Defaults to "j_username". */ public void setUsernameParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.usernameParameter = usernameParameter; } /** * Sets the parameter name which will be used to obtain the password from the login request.. * * @param passwordParameter the parameter name. Defaults to "j_password". */ public void setPasswordParameter(String passwordParameter) { Assert.hasText(passwordParameter, "Password parameter must not be empty or null"); this.passwordParameter = passwordParameter; } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); /* *該處理器實現了 AuthenticationFailureHandler *用於處理登錄失敗後,跳轉的界面 */ this.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler); } /** * Defines whether only HTTP POST requests will be allowed by this filter. * If set to true, and an authentication request is received which is not a POST request, an exception will * be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method * will be called as if handling a failed authentication. * <p> * Defaults to <tt>true</tt> but may be overridden by subclasses. */ public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getUsernameParameter() { return usernameParameter; } public final String getPasswordParameter() { return passwordParameter; } public String getCompanyCode() { return companyCode; } public void setCompanyCode(String companyCode) { this.companyCode = companyCode; } public SimpleUrlAuthenticationFailureHandler getSimpleUrlAuthenticationFailureHandler() { return simpleUrlAuthenticationFailureHandler; } public void setSimpleUrlAuthenticationFailureHandler( SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler) { this.simpleUrlAuthenticationFailureHandler = simpleUrlAuthenticationFailureHandler; } }
這樣就能使登陸錯誤成功返回登陸頁面,問題得以解決。