View Javadoc
1   package cn.home1.oss.lib.security.internal.template;
2   
3   import static cn.home1.oss.lib.common.CodecUtils.urlEncode;
4   
5   import cn.home1.oss.lib.errorhandle.api.ExceptionResolver;
6   import cn.home1.oss.lib.errorhandle.api.ResolvedError;
7   import cn.home1.oss.lib.webmvc.api.TypeSafeCookie;
8   
9   import lombok.extern.slf4j.Slf4j;
10  
11  import org.springframework.security.core.AuthenticationException;
12  import org.springframework.security.web.DefaultRedirectStrategy;
13  import org.springframework.security.web.RedirectStrategy;
14  import org.springframework.security.web.WebAttributes;
15  import org.springframework.security.web.authentication.AuthenticationFailureHandler;
16  import org.springframework.security.web.util.UrlUtils;
17  import org.springframework.util.Assert;
18  
19  import java.io.IOException;
20  
21  import javax.servlet.ServletException;
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpServletResponse;
24  import javax.servlet.http.HttpSession;
25  
26  /**
27   * Created by zhanghaolun on 16/8/23.
28   */
29  @Slf4j
30  public class TemplateAuthenticationFailureHandler implements AuthenticationFailureHandler {
31    // extends SimpleUrlAuthenticationFailureHandler
32  
33    private final ExceptionResolver<Throwable> exceptionResolver;
34    private final TypeSafeCookie<ResolvedError> resolvedErrorCookie;
35  
36    private String defaultFailureUrl;
37    private boolean forwardToDestination;
38    private boolean allowSessionCreation = true;
39    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
40  
41    public TemplateAuthenticationFailureHandler( //
42      final String defaultFailureUrl, //
43      final ExceptionResolver<Throwable> exceptionResolver, //
44      final TypeSafeCookie<ResolvedError> resolvedErrorCookie //
45    ) {
46      this.setDefaultFailureUrl(defaultFailureUrl);
47      this.exceptionResolver = exceptionResolver;
48      this.resolvedErrorCookie = resolvedErrorCookie;
49    }
50  
51    @Override
52    public void onAuthenticationFailure( //
53      final HttpServletRequest request, //
54      final HttpServletResponse response, //
55      final AuthenticationException exception //
56    ) throws IOException, ServletException {
57      final ResolvedError resolvedError = this.exceptionResolver.resolve(request, exception);
58      if (this.resolvedErrorCookie != null) {
59        this.resolvedErrorCookie.setCookie(request, response, resolvedError.eraseTraces());
60      }
61  
62      if (this.defaultFailureUrl == null) {
63        if (log.isDebugEnabled()) {
64          log.debug("No failure URL set, sending 401 Unauthorized error");
65        }
66  
67        response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
68          "Authentication Failed: " + exception.getMessage());
69      } else {
70        saveException(request, exception);
71  
72        if (this.forwardToDestination) {
73          if (log.isDebugEnabled()) {
74            log.debug("Forwarding to " + this.defaultFailureUrl);
75          }
76  
77          request.getRequestDispatcher(this.defaultFailureUrl)
78            .forward(request, response);
79        } else {
80          final String url = this.defaultFailureUrl + "?error=" + urlEncode(resolvedError.getLocalizedMessage());
81          if (log.isDebugEnabled()) {
82            log.debug("Redirecting to " + url);
83          }
84          this.redirectStrategy.sendRedirect(request, response, url);
85        }
86      }
87    }
88  
89    /**
90     * <p>
91     * Caches the {@code AuthenticationException} for use in view rendering.
92     * </p>
93     * If {@code forwardToDestination} is set to true, request scope will be used,
94     * otherwise it will attempt to store the exception in the session. If there is no
95     * session and {@code allowSessionCreation} is {@code true} a session will be created.
96     * Otherwise the exception will not be stored.
97     *
98     * @param request   request
99     * @param exception exception
100    */
101   protected final void saveException(final HttpServletRequest request, final AuthenticationException exception) {
102     if (this.forwardToDestination) {
103       request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
104     } else {
105       final HttpSession session = request.getSession(false);
106 
107       if (session != null || this.allowSessionCreation) {
108         request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
109       }
110     }
111   }
112 
113   /**
114    * The URL which will be used as the failure destination.
115    *
116    * @param defaultFailureUrl the failure URL, for example "/loginFailed.jsp".
117    */
118   public final void setDefaultFailureUrl(final String defaultFailureUrl) {
119     Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl), "'"
120       + defaultFailureUrl + "' is not a valid redirect URL");
121     this.defaultFailureUrl = defaultFailureUrl;
122   }
123 
124   protected boolean isUseForward() {
125     return this.forwardToDestination;
126   }
127 
128   /**
129    * If set to <tt>true</tt>, performs a forward to the failure destination URL instead
130    * of a redirect. Defaults to <tt>false</tt>.
131    *
132    * @param forwardToDestination forwardToDestination
133    */
134   public void setUseForward(final boolean forwardToDestination) {
135     this.forwardToDestination = forwardToDestination;
136   }
137 
138   /**
139    * Allows overriding of the behaviour when redirecting to a target URL.
140    *
141    * @param redirectStrategy redirectStrategy
142    */
143   public void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
144     this.redirectStrategy = redirectStrategy;
145   }
146 
147   protected RedirectStrategy getRedirectStrategy() {
148     return this.redirectStrategy;
149   }
150 
151   protected boolean isAllowSessionCreation() {
152     return this.allowSessionCreation;
153   }
154 
155   public void setAllowSessionCreation(final boolean allowSessionCreation) {
156     this.allowSessionCreation = allowSessionCreation;
157   }
158 
159   public static TemplateAuthenticationFailureHandler templateFailureHandler( //
160     final String loginFormUrl, //
161     final ExceptionResolver<Throwable> exceptionResolver, //
162     final TypeSafeCookie<ResolvedError> resolvedErrorCookie //
163   ) {
164     final TemplateAuthenticationFailureHandler failureHandler = new TemplateAuthenticationFailureHandler( //
165       loginFormUrl, exceptionResolver, resolvedErrorCookie);
166     failureHandler.setUseForward(false);
167     failureHandler.setRedirectStrategy(new SmartRedirectStrategy());
168     return failureHandler;
169   }
170 }