一、看下内部原理
简化后的认证过程分为7步:
- 用户访问网站,打开了一个链接(origin url)。
- 请求发送给服务器,服务器判断用户请求了受保护的资源。
- 由于用户没有登录,服务器重定向到登录页面
- 填写表单,点击登录
- 浏览器将用户名密码以表单形式发送给服务器
- 服务器验证用户名密码。成功,进入到下一步。否则要求用户重新认证(第三步)
- 服务器对用户拥有的权限(角色)判定: 有权限,重定向到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步:
- 用户访问网站,打开了一个链接(origin url)。
- 请求发送给服务器,服务器判断用户请求了受保护的资源。
- 由于用户没有登录,服务器重定向到登录页面
- 填写表单,点击登录
- 浏览器将用户名密码以表单形式发送给服务器
- 服务器验证用户名密码。成功,进入到下一步。否则要求用户重新认证(第三步)
- 服务器对用户拥有的权限(角色)判定: 有权限,重定向到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. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理
- 规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理
- 规则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.