一、看下内部原理

简化后的认证过程分为7步:

  1. 用户访问网站,打开了一个链接(origin url)。
  2. 请求发送给服务器,服务器判断用户请求了受保护的资源。
  3. 由于用户没有登录,服务器重定向到登录页面
  4. 填写表单,点击登录
  5. 浏览器将用户名密码以表单形式发送给服务器
  6. 服务器验证用户名密码。成功,进入到下一步。否则要求用户重新认证(第三步)
  7. 服务器对用户拥有的权限(角色)判定: 有权限,重定向到origin url; 权限不足,返回状态码403("forbidden").

从第3步,我们可以知道,用户的请求被中断了。

用户登录成功后(第7步),会被重定向到origin url,spring security通过使用缓存的request,使得被中断的请求能够继续执行。

 

使用缓存

用户登录成功后,页面重定向到origin url。浏览器发出的请求优先被拦截器RequestCacheAwareFilter拦截,RequestCacheAwareFilter通过其持有的RequestCache对象实现request的恢复。



[java]  view plain  copy

 


1. public void doFilter(ServletRequest request, ServletResponse response,  
2. throws IOException, ServletException {  
3.           
4. // request匹配,则取出,该操作同时会将缓存的request从session中删除  
5.         HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(  
6.                 (HttpServletRequest) request, (HttpServletResponse) response);  
7.           
8. // 优先使用缓存的request  
9. null ? request : wrappedSavedRequest,  
10.                 response);  
11.     }


 

何时缓存

首先,我们需要了解下RequestCache以及ExceptionTranslationFilter。

RequestCache

RequestCache接口声明了缓存与恢复操作。默认实现类是HttpSessionRequestCache。HttpSessionRequestCache的实现比较简单,这里只列出接口的声明:

 



[java]  view plain  copy

 



    1. public interface RequestCache {  
    2.   
    3. // 将request缓存到session中  
    4. void saveRequest(HttpServletRequest request, HttpServletResponse response);  
    5.       
    6. // 从session中取request  
    7.     SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);  
    8.       
    9. // 获得与当前request匹配的缓存,并将匹配的request从session中删除  
    10.     HttpServletRequest getMatchingRequest(HttpServletRequest request,  
    11.             HttpServletResponse response);  
    12.       
    13. // 删除缓存的request  
    14. void removeRequest(HttpServletRequest request, HttpServletResponse response);  
    15. }



     

     

     

     

     

     

    ExceptionTranslationFilter

    ExceptionTranslationFilter 是Spring Security的核心filter之一,用来处理AuthenticationException和AccessDeniedException两种异常。

    在我们的例子中,AuthenticationException指的是未登录状态下访问受保护资源,AccessDeniedException指的是登陆了但是由于权限不足(比如普通用户访问管理员界面)。

    ExceptionTranslationFilter 持有两个处理类,分别是AuthenticationEntryPoint和AccessDeniedHandler。

    ExceptionTranslationFilter 对异常的处理是通过这两个处理类实现的,处理规则很简单:



    [java]  view plain  copy

     


    1. 规则1. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理  
    2. 规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理  
    3. 规则3. 如果异常是 AccessDeniedException 且用户不是匿名用户,如果否则交给 AccessDeniedHandler 处理。

    对应以下代码



    [java]  view plain  copy

     



      1. private void handleSpringSecurityException(HttpServletRequest request,  
      2.             HttpServletResponse response, FilterChain chain, RuntimeException exception)  
      3. throws IOException, ServletException {  
      4. if (exception instanceof AuthenticationException) {  
      5.             logger.debug(  
      6. "Authentication exception occurred; redirecting to authentication entry point",  
      7.                     exception);  
      8.   
      9.             sendStartAuthentication(request, response, chain,  
      10.                     (AuthenticationException) exception);  
      11.         }  
      12. else if (exception instanceof AccessDeniedException) {  
      13. if (authenticationTrustResolver.isAnonymous(SecurityContextHolder  
      14.                     .getContext().getAuthentication())) {  
      15.                 logger.debug(  
      16. "Access is denied (user is anonymous); redirecting to authentication entry point",  
      17.                         exception);  
      18.   
      19.                 sendStartAuthentication(  
      20.                         request,  
      21.                         response,  
      22.                         chain,  
      23. new InsufficientAuthenticationException(  
      24. "Full authentication is required to access this resource"));  
      25.             }  
      26. else {  
      27.                 logger.debug(  
      28. "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",  
      29.                         exception);  
      30.   
      31.                 accessDeniedHandler.handle(request, response,  
      32.                         (AccessDeniedException) exception);  
      33.             }  
      34.         }  
      35.     }



       

      AccessDeniedHandler 默认实现是 AccessDeniedHandlerImpl。该类对异常的处理是返回403错误码。



      [java]  view plain  copy

       

      1. public void handle(HttpServletRequest request, HttpServletResponse response,  
      2. throws IOException,  
      3.             ServletException {  
      4. if (!response.isCommitted()) {  
      5. if (errorPage != null) {  // 定义了errorPage  
      6. // errorPage中可以操作该异常  
      7.             request.setAttribute(WebAttributes.ACCESS_DENIED_403,  
      8.                     accessDeniedException);  
      9.   
      10. // 设置403状态码  
      11.             response.setStatus(HttpServletResponse.SC_FORBIDDEN);  
      12.   
      13. // 转发到errorPage  
      14.             RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);  
      15.             dispatcher.forward(request, response);  
      16.         }  
      17. else { // 没有定义errorPage,则返回403状态码(Forbidden),以及错误信息  
      18.             response.sendError(HttpServletResponse.SC_FORBIDDEN,  
      19.                     accessDeniedException.getMessage());  
      20.         }  
      21.     }  
      22. }


       

      AuthenticationEntryPoint 默认实现是 LoginUrlAuthenticationEntryPoint, 该类的处理是转发或重定向到登录页面

       



      [java]  view plain  copy

       



        1. public void commence(HttpServletRequest request, HttpServletResponse response,  
        2. throws IOException, ServletException {  
        3.   
        4. null;  
        5.   
        6. if (useForward) {  
        7.   
        8. if (forceHttps && "http".equals(request.getScheme())) {  
        9. // First redirect the current request to HTTPS.  
        10. // When that request is received, the forward to the login page will be  
        11. // used.  
        12.             redirectUrl = buildHttpsRedirectUrlForRequest(request);  
        13.         }  
        14.   
        15. if (redirectUrl == null) {  
        16.             String loginForm = determineUrlToUseForThisRequest(request, response,  
        17.                     authException);  
        18.   
        19. if (logger.isDebugEnabled()) {  
        20. "Server side forward to: " + loginForm);  
        21.             }  
        22.   
        23.             RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);  
        24.       
        25. // 转发  
        26.             dispatcher.forward(request, response);  
        27.   
        28. return;  
        29.         }  
        30.     }  
        31. else {  
        32. // redirect to login page. Use https if forceHttps true  
        33.   
        34.         redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);  
        35.   
        36.     }  
        37.       
        38. // 重定向  
        39.     redirectStrategy.sendRedirect(request, response, redirectUrl);  
        40. }



         

         

         

         

         

         

        了解完这些,回到我们的例子。

        第3步时,用户未登录的情况下访问受保护资源,ExceptionTranslationFilter会捕获到AuthenticationException异常(规则1)。页面需要跳转,ExceptionTranslationFilter在跳转前使用requestCache缓存request。



        [java]  view plain  copy

         



          1. protected void sendStartAuthentication(HttpServletRequest request,  
          2.             HttpServletResponse response, FilterChain chain,  
          3. throws ServletException, IOException {  
          4. // SEC-112: Clear the SecurityContextHolder's Authentication, as the  
          5. // existing Authentication is no longer considered valid  
          6. null);  
          7. // 缓存 request  
          8.     requestCache.saveRequest(request, response);  
          9. "Calling Authentication entry point.");  
          10.     authenticationEntryPoint.commence(request, response, reason);  
          11. }


          二、了解了以上原理以及上篇的forward和redirect的区别,配置实现如下,基于springsecurity4.1.3版本

          配置文件:完整的

           



          [html]  view plain  copy

           


          1. <?xml version="1.0" encoding="UTF-8"?>  
          2.   
          3. <beans:beans xmlns="http://www.springframework.org/schema/security"  
          4. xmlns:beans="http://www.springframework.org/schema/beans"  
          5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
          6. xsi:schemaLocation="http://www.springframework.org/schema/beans  
          7.         http://www.springframework.org/schema/beans/spring-beans.xsd  
          8.         http://www.springframework.org/schema/security  
          9. >  
          10.           
          11. <http auto-config="true" use-expressions="true" entry-point-ref="myLoginUrlAuthenticationEntryPoint">  
          12. <form-login   
          13. login-page="/login"  
          14. authentication-failure-url="/login?error"   
          15. login-processing-url="/login"  
          16. authentication-success-handler-ref="myAuthenticationSuccessHandler" />     
          17. <!-- 认证成功用自定义类myAuthenticationSuccessHandler处理 -->  
          18.            
          19. <logout logout-url="/logout"   
          20. logout-success-url="/"   
          21. invalidate-session="true"  
          22. delete-cookies="JSESSIONID"/>  
          23.           
          24. <csrf disabled="true" />  
          25. <intercept-url pattern="/order/*" access="hasRole('ROLE_USER')"/>  
          26. </http>  
          27.       
          28. <!-- 使用自定义类myUserDetailsService从数据库获取用户信息 -->  
          29. <authentication-manager>    
          30. <authentication-provider user-service-ref="myUserDetailsService">    
          31. <!-- 加密 -->  
          32. <password-encoder hash="md5">  
          33. </password-encoder>  
          34. </authentication-provider>  
          35. </authentication-manager>  
          36.       
          37. <!-- 被认证请求向登录界面跳转采用forward方式 -->  
          38. <beans:bean id="myLoginUrlAuthenticationEntryPoint"   
          39. class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
          40. <beans:constructor-arg name="loginFormUrl" value="/login"></beans:constructor-arg>  
          41. <beans:property name="useForward" value="true"/>  
          42. </beans:bean>  
          43.       
          44. </beans:beans>


          主要配置


           

           

           

           



          [html]  view plain  copy

           



            1. <pre code_snippet_id="1902646" snippet_file_name="blog_20160927_9_7050170" class="html" name="code"><http auto-config="true" use-expressions="true" entry-point-ref="myLoginUrlAuthenticationEntryPoint">  
            2.   
            3. <!-- 被认证请求向登录界面跳转采用forward方式 -->  
            4. <beans:bean id="myLoginUrlAuthenticationEntryPoint"   
            5. class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
            6. <beans:constructor-arg name="loginFormUrl" value="/login"></beans:constructor-arg>  
            7. <beans:property name="useForward" value="true"/>  
            8. </beans:bean></pre><br>  
            9. <pre></pre>  
            10. <p>从上面的分析可知,默认情况下采用的是redirect方式,这里通过配置从而实现了forward方式,这里还是直接利用的security自带的类LoginUrlAuthenticationEntryPoint,只不过进行了以上配置:</p>  
            11. <p></p><pre code_snippet_id="1902646" snippet_file_name="blog_20161215_10_1004934" class="java" name="code">/**  
            12.      * Performs the redirect (or forward) to the login form URL.  
            13.      */  
            14.     public void commence(HttpServletRequest request, HttpServletResponse response,  
            15.             AuthenticationException authException) throws IOException, ServletException {  
            16.   
            17. redirectUrl = null;  
            18.   
            19.         if (useForward) {  
            20.   
            21.             if (forceHttps && "http".equals(request.getScheme())) {  
            22.                 // First redirect the current request to HTTPS.  
            23.                 // When that request is received, the forward to the login page will be  
            24.                 // used.  
            25. redirectUrl = buildHttpsRedirectUrlForRequest(request);  
            26.             }  
            27.   
            28. redirectUrl == null) {  
            29. loginForm = determineUrlToUseForThisRequest(request, response,  
            30.                         authException);  
            31.   
            32.                 if (logger.isDebugEnabled()) {  
            33.                     logger.debug("Server side forward to: " + loginForm);  
            34.                 }  
            35.   
            36. dispatcher = request.getRequestDispatcher(loginForm);  
            37.   
            38.                 dispatcher.forward(request, response);  
            39.   
            40.                 return;  
            41.             }  
            42.         }  
            43.         else {  
            44.             // redirect to login page. Use https if forceHttps true  
            45.   
            46. redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);  
            47.   
            48.         }  
            49.   
            50.         redirectStrategy.sendRedirect(request, response, redirectUrl);  
            51. </pre><br>  
            52. <p></p>  
            53. <p></p>  
            54. <p></p>  
            55. <p></p>  
            56. <p>登录成功后的类配置,存入登录user信息后交给认证成功后的处理类MyAuthenticationSuccessHandler,该类集成了SavedRequestAwareAuthenticationSuccessHandler,他会从缓存中提取请求,从而可以恢复之前请求的数据</p>  
            57. <p></p><pre code_snippet_id="1902646" snippet_file_name="blog_20161215_11_4222490" class="java" name="code">/**  
            58.  * 登录后操作  
            59.  *   
            60.  * @author HHL  
            61.  * @date  
            62.  *   
            63.  */  
            64. @Component  
            65. public class MyAuthenticationSuccessHandler extends  
            66.         SavedRequestAwareAuthenticationSuccessHandler {  
            67.   
            68.     @Autowired  
            69.     private IUserService userService;  
            70.   
            71.     @Override  
            72.     public void onAuthenticationSuccess(HttpServletRequest request,  
            73.             HttpServletResponse response, Authentication authentication)  
            74.             throws IOException, ServletException {  
            75.   
            76.         // 认证成功后,获取用户信息并添加到session中  
            77. userDetails = (UserDetails) authentication.getPrincipal();  
            78. user = userService.getUserByName(userDetails.getUsername());  
            79.         request.getSession().setAttribute("user", user);  
            80.           
            81.         super.onAuthenticationSuccess(request, response, authentication);  
            82.       
            83.     }  
            84.   
            85.   
            86. }</pre><p></p>  
            87. <p></p>  
            88. <p></p>  
            89. <p></p>  
            90. <p>SavedRequestAwareAuthenticationSuccessHandler中的onAuthenticationSuccess方法;</p>  
            91. <p></p><pre code_snippet_id="1902646" snippet_file_name="blog_20161215_12_7440047" class="java" name="code">@Override  
            92.     public void onAuthenticationSuccess(HttpServletRequest request,  
            93.             HttpServletResponse response, Authentication authentication)  
            94.             throws ServletException, IOException {  
            95. savedRequest = requestCache.getRequest(request, response);  
            96.   
            97. savedRequest == null) {  
            98.             super.onAuthenticationSuccess(request, response, authentication);  
            99.   
            100.             return;  
            101.         }  
            102. targetUrlParameter = getTargetUrlParameter();  
            103.         if (isAlwaysUseDefaultTargetUrl()  
            104.                 || (targetUrlParameter != null && StringUtils.hasText(request  
            105.                         .getParameter(targetUrlParameter)))) {  
            106.             requestCache.removeRequest(request, response);  
            107.             super.onAuthenticationSuccess(request, response, authentication);  
            108.   
            109.             return;  
            110.         }  
            111.   
            112.         clearAuthenticationAttributes(request);  
            113.   
            114.         // Use the DefaultSavedRequest URL  
            115. targetUrl = savedRequest.getRedirectUrl();  
            116.         logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);  
            117.         getRedirectStrategy().sendRedirect(request, response, targetUrl);  
            118. </pre>4.1.3中如果默认不配置的话也是采用的SavedRequestAwareAuthenticationSuccessHandler进行处理,详情可参见:<a target="_blank" href="">Spring实战篇系列----源码解析Spring Security中的过滤器Filter初始化  
            119. </a><br>  
            120. <br>  
            121. <br>  
            122. 上述实现了跳转到登录界面采用forward方式,就是浏览器地址栏没有变化,当然也可采用redirect方式,地址栏变为登录界面地址栏,当登录完成后恢复到原先的请求页面,请求信息会从requestCache中还原回来。可参考<a target="_blank" href=""> Spring实战篇系列----spring security4.1.3配置以及踩过的坑</a><br>  
            123. <p></p>  
            124. <p></p>  
            125. <p></p>  
            126. <p></p>  
            127. <p></p>  
            128. <p></p>  
            129. <p><br>  
            130. </p>  
            131. <p><br>  
            132. </p>  
            133. <p>参考:</p>  
            134. <p><a target="_blank" href=""></a></p>  
            135. <p><a target="_blank" href="http://gtbald.iteye.com/blog/1214132">http://gtbald.iteye.com/blog/1214132</a></p>  
            136. <p><br>  
            137. <br>  
            138. <br>  
            139. </p>  
            140. <div style="top:0px"></div>  
            141. <div style="top:4827px"></div>  
            142. <div style="top:3353px"></div>  
            143.


             



             



             


            一、看下内部原理

            简化后的认证过程分为7步:

            1. 用户访问网站,打开了一个链接(origin url)。
            2. 请求发送给服务器,服务器判断用户请求了受保护的资源。
            3. 由于用户没有登录,服务器重定向到登录页面
            4. 填写表单,点击登录
            5. 浏览器将用户名密码以表单形式发送给服务器
            6. 服务器验证用户名密码。成功,进入到下一步。否则要求用户重新认证(第三步)
            7. 服务器对用户拥有的权限(角色)判定: 有权限,重定向到origin url; 权限不足,返回状态码403("forbidden").

            从第3步,我们可以知道,用户的请求被中断了。

            用户登录成功后(第7步),会被重定向到origin url,spring security通过使用缓存的request,使得被中断的请求能够继续执行。

             

            使用缓存

            用户登录成功后,页面重定向到origin url。浏览器发出的请求优先被拦截器RequestCacheAwareFilter拦截,RequestCacheAwareFilter通过其持有的RequestCache对象实现request的恢复。



            [java]  view plain  copy

             



              1. public void doFilter(ServletRequest request, ServletResponse response,  
              2. throws IOException, ServletException {  
              3.           
              4. // request匹配,则取出,该操作同时会将缓存的request从session中删除  
              5.         HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(  
              6.                 (HttpServletRequest) request, (HttpServletResponse) response);  
              7.           
              8. // 优先使用缓存的request  
              9. null ? request : wrappedSavedRequest,  
              10.                 response);  
              11.     }


               

              何时缓存

              首先,我们需要了解下RequestCache以及ExceptionTranslationFilter。

              RequestCache

              RequestCache接口声明了缓存与恢复操作。默认实现类是HttpSessionRequestCache。HttpSessionRequestCache的实现比较简单,这里只列出接口的声明:

               



              [java]  view plain  copy

               


              1. public interface RequestCache {  
              2.   
              3. // 将request缓存到session中  
              4. void saveRequest(HttpServletRequest request, HttpServletResponse response);  
              5.       
              6. // 从session中取request  
              7.     SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);  
              8.       
              9. // 获得与当前request匹配的缓存,并将匹配的request从session中删除  
              10.     HttpServletRequest getMatchingRequest(HttpServletRequest request,  
              11.             HttpServletResponse response);  
              12.       
              13. // 删除缓存的request  
              14. void removeRequest(HttpServletRequest request, HttpServletResponse response);  
              15. }


               

               

               

               

               

               

              ExceptionTranslationFilter

              ExceptionTranslationFilter 是Spring Security的核心filter之一,用来处理AuthenticationException和AccessDeniedException两种异常。

              在我们的例子中,AuthenticationException指的是未登录状态下访问受保护资源,AccessDeniedException指的是登陆了但是由于权限不足(比如普通用户访问管理员界面)。

              ExceptionTranslationFilter 持有两个处理类,分别是AuthenticationEntryPoint和AccessDeniedHandler。

              ExceptionTranslationFilter 对异常的处理是通过这两个处理类实现的,处理规则很简单:



              [java]  view plain  copy

               



              1. 规则1. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理  
              2. 规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理  
              3. 规则3. 如果异常是 AccessDeniedException 且用户不是匿名用户,如果否则交给 AccessDeniedHandler 处理。  



              对应以下代码



              [java]  view plain  copy

               


              1. private void handleSpringSecurityException(HttpServletRequest request,  
              2.             HttpServletResponse response, FilterChain chain, RuntimeException exception)  
              3. throws IOException, ServletException {  
              4. if (exception instanceof AuthenticationException) {  
              5.             logger.debug(  
              6. "Authentication exception occurred; redirecting to authentication entry point",  
              7.                     exception);  
              8.   
              9.             sendStartAuthentication(request, response, chain,  
              10.                     (AuthenticationException) exception);  
              11.         }  
              12. else if (exception instanceof AccessDeniedException) {  
              13. if (authenticationTrustResolver.isAnonymous(SecurityContextHolder  
              14.                     .getContext().getAuthentication())) {  
              15.                 logger.debug(  
              16. "Access is denied (user is anonymous); redirecting to authentication entry point",  
              17.                         exception);  
              18.   
              19.                 sendStartAuthentication(  
              20.                         request,  
              21.                         response,  
              22.                         chain,  
              23. new InsufficientAuthenticationException(  
              24. "Full authentication is required to access this resource"));  
              25.             }  
              26. else {  
              27.                 logger.debug(  
              28. "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",  
              29.                         exception);  
              30.   
              31.                 accessDeniedHandler.handle(request, response,  
              32.                         (AccessDeniedException) exception);  
              33.             }  
              34.         }  
              35.     }


               

              AccessDeniedHandler 默认实现是 AccessDeniedHandlerImpl。该类对异常的处理是返回403错误码。



              [java]  view plain  copy

               



              1. public void handle(HttpServletRequest request, HttpServletResponse response,  
              2. throws IOException,  
              3.             ServletException {  
              4. if (!response.isCommitted()) {  
              5. if (errorPage != null) {  // 定义了errorPage  
              6. // errorPage中可以操作该异常  
              7.             request.setAttribute(WebAttributes.ACCESS_DENIED_403,  
              8.                     accessDeniedException);  
              9.   
              10. // 设置403状态码  
              11.             response.setStatus(HttpServletResponse.SC_FORBIDDEN);  
              12.   
              13. // 转发到errorPage  
              14.             RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);  
              15.             dispatcher.forward(request, response);  
              16.         }  
              17. else { // 没有定义errorPage,则返回403状态码(Forbidden),以及错误信息  
              18.             response.sendError(HttpServletResponse.SC_FORBIDDEN,  
              19.                     accessDeniedException.getMessage());  
              20.         }  
              21.     }  
              22. }



               

              AuthenticationEntryPoint 默认实现是 LoginUrlAuthenticationEntryPoint, 该类的处理是转发或重定向到登录页面

               



              [java]  view plain  copy

               


              1. public void commence(HttpServletRequest request, HttpServletResponse response,  
              2. throws IOException, ServletException {  
              3.   
              4. null;  
              5.   
              6. if (useForward) {  
              7.   
              8. if (forceHttps && "http".equals(request.getScheme())) {  
              9. // First redirect the current request to HTTPS.  
              10. // When that request is received, the forward to the login page will be  
              11. // used.  
              12.             redirectUrl = buildHttpsRedirectUrlForRequest(request);  
              13.         }  
              14.   
              15. if (redirectUrl == null) {  
              16.             String loginForm = determineUrlToUseForThisRequest(request, response,  
              17.                     authException);  
              18.   
              19. if (logger.isDebugEnabled()) {  
              20. "Server side forward to: " + loginForm);  
              21.             }  
              22.   
              23.             RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);  
              24.       
              25. // 转发  
              26.             dispatcher.forward(request, response);  
              27.   
              28. return;  
              29.         }  
              30.     }  
              31. else {  
              32. // redirect to login page. Use https if forceHttps true  
              33.   
              34.         redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);  
              35.   
              36.     }  
              37.       
              38. // 重定向  
              39.     redirectStrategy.sendRedirect(request, response, redirectUrl);  
              40. }


               

               

               

               

               

               

              了解完这些,回到我们的例子。

              第3步时,用户未登录的情况下访问受保护资源,ExceptionTranslationFilter会捕获到AuthenticationException异常(规则1)。页面需要跳转,ExceptionTranslationFilter在跳转前使用requestCache缓存request。



              [java]  view plain  copy

               


              1. protected void sendStartAuthentication(HttpServletRequest request,  
              2.             HttpServletResponse response, FilterChain chain,  
              3. throws ServletException, IOException {  
              4. // SEC-112: Clear the SecurityContextHolder's Authentication, as the  
              5. // existing Authentication is no longer considered valid  
              6. null);  
              7. // 缓存 request  
              8.     requestCache.saveRequest(request, response);  
              9. "Calling Authentication entry point.");  
              10.     authenticationEntryPoint.commence(request, response, reason);  
              11. }


              二、了解了以上原理以及上篇的forward和redirect的区别,配置实现如下,基于springsecurity4.1.3版本

              配置文件:完整的

               



              [html]  view plain  copy

               


              1. <?xml version="1.0" encoding="UTF-8"?>  
              2.   
              3. <beans:beans xmlns="http://www.springframework.org/schema/security"  
              4. xmlns:beans="http://www.springframework.org/schema/beans"  
              5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
              6. xsi:schemaLocation="http://www.springframework.org/schema/beans  
              7.         http://www.springframework.org/schema/beans/spring-beans.xsd  
              8.         http://www.springframework.org/schema/security  
              9. >  
              10.           
              11. <http auto-config="true" use-expressions="true" entry-point-ref="myLoginUrlAuthenticationEntryPoint">  
              12. <form-login   
              13. login-page="/login"  
              14. authentication-failure-url="/login?error"   
              15. login-processing-url="/login"  
              16. authentication-success-handler-ref="myAuthenticationSuccessHandler" />     
              17. <!-- 认证成功用自定义类myAuthenticationSuccessHandler处理 -->  
              18.            
              19. <logout logout-url="/logout"   
              20. logout-success-url="/"   
              21. invalidate-session="true"  
              22. delete-cookies="JSESSIONID"/>  
              23.           
              24. <csrf disabled="true" />  
              25. <intercept-url pattern="/order/*" access="hasRole('ROLE_USER')"/>  
              26. </http>  
              27.       
              28. <!-- 使用自定义类myUserDetailsService从数据库获取用户信息 -->  
              29. <authentication-manager>    
              30. <authentication-provider user-service-ref="myUserDetailsService">    
              31. <!-- 加密 -->  
              32. <password-encoder hash="md5">  
              33. </password-encoder>  
              34. </authentication-provider>  
              35. </authentication-manager>  
              36.       
              37. <!-- 被认证请求向登录界面跳转采用forward方式 -->  
              38. <beans:bean id="myLoginUrlAuthenticationEntryPoint"   
              39. class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
              40. <beans:constructor-arg name="loginFormUrl" value="/login"></beans:constructor-arg>  
              41. <beans:property name="useForward" value="true"/>  
              42. </beans:bean>  
              43.       
              44. </beans:beans>


              主要配置


               

               

               

               



              [html]  view plain  copy

               


              1. <pre code_snippet_id="1902646" snippet_file_name="blog_20160927_9_7050170" class="html" name="code"><http auto-config="true" use-expressions="true" entry-point-ref="myLoginUrlAuthenticationEntryPoint">  
              2.   
              3. <!-- 被认证请求向登录界面跳转采用forward方式 -->  
              4. <beans:bean id="myLoginUrlAuthenticationEntryPoint"   
              5. class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
              6. <beans:constructor-arg name="loginFormUrl" value="/login"></beans:constructor-arg>  
              7. <beans:property name="useForward" value="true"/>  
              8. </beans:bean></pre><br>  
              9. <pre></pre>  
              10. <p>从上面的分析可知,默认情况下采用的是redirect方式,这里通过配置从而实现了forward方式,这里还是直接利用的security自带的类LoginUrlAuthenticationEntryPoint,只不过进行了以上配置:</p>  
              11. <p></p><pre code_snippet_id="1902646" snippet_file_name="blog_20161215_10_1004934" class="java" name="code">/**  
              12.      * Performs the redirect (or forward) to the login form URL.  
              13.      */  
              14.     public void commence(HttpServletRequest request, HttpServletResponse response,  
              15.             AuthenticationException authException) throws IOException, ServletException {  
              16.   
              17. redirectUrl = null;  
              18.   
              19.         if (useForward) {  
              20.   
              21.             if (forceHttps && "http".equals(request.getScheme())) {  
              22.                 // First redirect the current request to HTTPS.  
              23.                 // When that request is received, the forward to the login page will be  
              24.                 // used.  
              25. redirectUrl = buildHttpsRedirectUrlForRequest(request);  
              26.             }  
              27.   
              28. redirectUrl == null) {  
              29. loginForm = determineUrlToUseForThisRequest(request, response,  
              30.                         authException);  
              31.   
              32.                 if (logger.isDebugEnabled()) {  
              33.                     logger.debug("Server side forward to: " + loginForm);  
              34.                 }  
              35.   
              36. dispatcher = request.getRequestDispatcher(loginForm);  
              37.   
              38.                 dispatcher.forward(request, response);  
              39.   
              40.                 return;  
              41.             }  
              42.         }  
              43.         else {  
              44.             // redirect to login page. Use https if forceHttps true  
              45.   
              46. redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);  
              47.   
              48.         }  
              49.   
              50.         redirectStrategy.sendRedirect(request, response, redirectUrl);  
              51. </pre><br>  
              52. <p></p>  
              53. <p></p>  
              54. <p></p>  
              55. <p></p>  
              56. <p>登录成功后的类配置,存入登录user信息后交给认证成功后的处理类MyAuthenticationSuccessHandler,该类集成了SavedRequestAwareAuthenticationSuccessHandler,他会从缓存中提取请求,从而可以恢复之前请求的数据</p>  
              57. <p></p><pre code_snippet_id="1902646" snippet_file_name="blog_20161215_11_4222490" class="java" name="code">/**  
              58.  * 登录后操作  
              59.  *   
              60.  * @author HHL  
              61.  * @date  
              62.  *   
              63.  */  
              64. @Component  
              65. public class MyAuthenticationSuccessHandler extends  
              66.         SavedRequestAwareAuthenticationSuccessHandler {  
              67.   
              68.     @Autowired  
              69.     private IUserService userService;  
              70.   
              71.     @Override  
              72.     public void onAuthenticationSuccess(HttpServletRequest request,  
              73.             HttpServletResponse response, Authentication authentication)  
              74.             throws IOException, ServletException {  
              75.   
              76.         // 认证成功后,获取用户信息并添加到session中  
              77. userDetails = (UserDetails) authentication.getPrincipal();  
              78. user = userService.getUserByName(userDetails.getUsername());  
              79.         request.getSession().setAttribute("user", user);  
              80.           
              81.         super.onAuthenticationSuccess(request, response, authentication);  
              82.       
              83.     }  
              84.   
              85.   
              86. }</pre><p></p>  
              87. <p></p>  
              88. <p></p>  
              89. <p></p>  
              90. <p>SavedRequestAwareAuthenticationSuccessHandler中的onAuthenticationSuccess方法;</p>  
              91. <p></p><pre code_snippet_id="1902646" snippet_file_name="blog_20161215_12_7440047" class="java" name="code">@Override  
              92.     public void onAuthenticationSuccess(HttpServletRequest request,  
              93.             HttpServletResponse response, Authentication authentication)  
              94.             throws ServletException, IOException {  
              95. savedRequest = requestCache.getRequest(request, response);  
              96.   
              97. savedRequest == null) {  
              98.             super.onAuthenticationSuccess(request, response, authentication);  
              99.   
              100.             return;  
              101.         }  
              102. targetUrlParameter = getTargetUrlParameter();  
              103.         if (isAlwaysUseDefaultTargetUrl()  
              104.                 || (targetUrlParameter != null && StringUtils.hasText(request  
              105.                         .getParameter(targetUrlParameter)))) {  
              106.             requestCache.removeRequest(request, response);  
              107.             super.onAuthenticationSuccess(request, response, authentication);  
              108.   
              109.             return;  
              110.         }  
              111.   
              112.         clearAuthenticationAttributes(request);  
              113.   
              114.         // Use the DefaultSavedRequest URL  
              115. targetUrl = savedRequest.getRedirectUrl();  
              116.         logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);  
              117.         getRedirectStrategy().sendRedirect(request, response, targetUrl);  
              118. </pre>4.1.3中如果默认不配置的话也是采用的SavedRequestAwareAuthenticationSuccessHandler进行处理,详情可参见:<a target="_blank" href="">Spring实战篇系列----源码解析Spring Security中的过滤器Filter初始化  
              119. </a><br>  
              120. <br>  
              121. <br>  
              122. 上述实现了跳转到登录界面采用forward方式,就是浏览器地址栏没有变化,当然也可采用redirect方式,地址栏变为登录界面地址栏,当登录完成后恢复到原先的请求页面,请求信息会从requestCache中还原回来。可参考<a target="_blank" href=""> Spring实战篇系列----spring security4.1.3配置以及踩过的坑</a><br>  
              123. <p></p>  
              124. <p></p>  
              125. <p></p>  
              126. <p></p>  
              127. <p></p>  
              128. <p></p>  
              129. <p><br>  
              130. </p>  
              131. <p><br>  
              132. </p>  
              133. <p>参考:</p>  
              134. <p><a target="_blank" href=""></a></p>  
              135. <p><a target="_blank" href="http://gtbald.iteye.com/blog/1214132">http://gtbald.iteye.com/blog/1214132</a></p>  
              136. <p><br>  
              137. <br>  
              138. <br>  
              139. </p>  
              140. <div style="top:0px"></div>  
              141. <div style="top:4827px"></div>  
              142. <div style="top:3353px"></div>  
              143.