如果用户在login后访问login页面,如何redirect到主页?

这是我的弹簧安全configuration:

<http pattern="/auth/login" security="none" /> <http pattern="/auth/loginFailed" security="none" /> <http pattern="/resources/**" security="none" /> <http auto-config="true" access-decision-manager-ref="accessDecisionManager"> <intercept-url pattern="/auth/logout" access="permitAll"/> <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS"/> <intercept-url pattern="/**" access="XYZ_ACCESS"/> <form-login login-page="/auth/login" authentication-failure-url="/auth/loginFailed" authentication-success-handler-ref="authenticationSuccessHandler" /> <logout logout-url="/auth/logout" logout-success-url="/auth/login" /> </http> 

authenticationSuccessHandler扩展了SavedRequestAwareAuthenticationSuccessHandler确保用户被redirect到他最初请求的页面。

但是,由于/auth/login被标记为security="none" ,如果用户在login后访问login页面,则无法将用户成功redirect到主页。我相信这也是正确的用户体验 。

我也尝试了下面的内容,但是Principal对象总是为null ,大概是因为security="none"属性。

 @RequestMapping(value = "/auth/login", method = GET) public String showLoginForm(HttpServletRequest request, Principal principal) { if(principal != null) { return "redirect:/"; } return "login"; } 

我已经比上次更深入地检查了这个话题,发现你必须确定用户是否在控制器中被自己authentication。 Row Winch(Spring Security dev) 在这里说 :

Spring Security没有意识到你的应用程序的内部(例如,如果你想让你的login页面是基于用户login或者不login)。 当请求login页面并且用户login时,要显示您的主页,请使用login页面(或其控制器)中的SecurityContextHolder ,并将用户redirect或转发到主页面。

所以解决方法是确定用户请求/auth/login是否是匿名的,如下所示。

applicationContext-security.xml

 <http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager"> <intercept-url pattern="/auth/login" access="permitAll" /> <intercept-url pattern="/auth/logout" access="permitAll" /> <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS" /> <intercept-url pattern="/**" access="XYZ_ACCESS" /> <form-login login-page="/auth/login" authentication-failure-url="/auth/loginFailed" authentication-success-handler-ref="authenticationSuccessHandler" /> <logout logout-url="/auth/logout" logout-success-url="/auth/login" /> </http> <beans:bean id="defaultTargetUrl" class="java.lang.String"> <beans:constructor-arg value="/content" /> </beans:bean> <beans:bean id="authenticationTrustResolver" class="org.springframework.security.authentication.AuthenticationTrustResolverImpl" /> <beans:bean id="authenticationSuccessHandler" class="com.example.spring.security.MyAuthenticationSuccessHandler"> <beans:property name="defaultTargetUrl" ref="defaultTargetUrl" /> </beans:bean> 

添加到applicationContext.xml的 bean定义:

 <bean id="securityContextAccessor" class="com.example.spring.security.SecurityContextAccessorImpl" /> 

这是class级

 public final class SecurityContextAccessorImpl implements SecurityContextAccessor { @Autowired private AuthenticationTrustResolver authenticationTrustResolver; @Override public boolean isCurrentAuthenticationAnonymous() { final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return authenticationTrustResolver.isAnonymous(authentication); } } 

实现简单的界面

 public interface SecurityContextAccessor { boolean isCurrentAuthenticationAnonymous(); } 

SecurityContextHolder访问代码是从控制器解耦,我遵循这个答案的build议 ,因此SecurityContextAccessor接口。)

最后但并非最不重要的控制器redirect逻辑:

 @Controller @RequestMapping("/auth") public class AuthController { @Autowired SecurityContextAccessor securityContextAccessor; @Autowired @Qualifier("defaultTargetUrl") private String defaultTargetUrl; @RequestMapping(value = "/login", method = RequestMethod.GET) public String login() { if (securityContextAccessor.isCurrentAuthenticationAnonymous()) { return "login"; } else { return "redirect:" + defaultTargetUrl; } } } 

定义defaultTargetUrl String bean看起来像一个黑客,但我没有更好的方式不去硬编码url …(实际上在我们的项目中,我们使用包含静态最终string字段的类<util:constant> )。 。

您也可以将您的login页面限制为ROLE_ANONYMOUS并设置<access-denied-handler />

 <access-denied-handler ref="accessDeniedHandler" /> <intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" /> 

并在您的处理程序中检查用户是否已经过身份validation:

 @Service public class AccessDeniedHandler extends AccessDeniedHandlerImpl { private final String HOME_PAGE = "/index.html"; @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) { response.sendRedirect(HOME_PAGE); } super.handle(request, response, e); } } 

为此目的实现一个redirect拦截器:

拦截器(实现HandlerInterceptor接口)检查是否有人尝试访问login页面,如果此人已经login,则拦截器将redirect发送到索引页面。

 public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter { private String[] loginPagePrefixes = new String[] { "/login" }; private String redirectUrl = "/index.html"; private UrlPathHelper urlPathHelper = new UrlPathHelper(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request)) && isAuthenticated()) { response.setContentType("text/plain"); sendRedirect(request, response); return false; } else { return true; } } private boolean isAuthenticated() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { return false; } if (authentication instanceof AnonymousAuthenticationToken) { return false; } return authentication.isAuthenticated(); } private void sendRedirect(HttpServletRequest request, HttpServletResponse response) { String encodedRedirectURL = response.encodeRedirectURL( request.getContextPath() + this.redirectUrl); response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT); response.setHeader("Location", encodedRedirectURL); } private boolean isInLoginPaths(final String requestUrl) { for (String login : this.loginPagePrefixes) { if (requestUrl.startsWith(login)) { return true; } } return false; } } 

您可以通过http元素中的access-denied-page属性保持简单的stream动,或者如同dtrunk说写入处理器拒绝访问一样。 configuration会像

 <http access-denied-page="/403" ... > <intercept-url pattern="/login" access="ROLE_ANONYMOUS" /> <intercept-url pattern="/user/**" access="ROLE_USER" /> <intercept-url pattern="/admin/**" access="ROLE_ADMIN" /> <form-login login-page="/login" default-target-url="/home" ... /> ... </http> 

/403控制器中

 @RequestMapping(value = "/403", method = RequestMethod.GET) public String accessDenied() { //simple impl return "redirect:/home"; } 

/home

 @RequestMapping(value = "/home", method = RequestMethod.GET) public String home(Authentication authentication) { // map as many home urls with Role Map<String, String> dashBoardUrls = new HashMap<String, String>(); dashBoardUrls.put("ROLE_USER", "/user/dashboard"); dashBoardUrls.put("ROLE_ADMIN", "/admin/dashboard"); String url = null; Collection<? extends GrantedAuthority> grants = authentication .getAuthorities(); // for one role per user for (GrantedAuthority grantedAuthority : grants) { url = dashBoardUrls.get(grantedAuthority.getAuthority()); } if (url == null) return "/errors/default_access_denied.jsp"; return "redirect:" + url; } 

当你在没有login的情况下提出/admin/dashboard请求时,它将通过安全性自动redirect/login

 <http pattern="/login" auto-config="true" disable-url-rewriting="true"> <intercept-url pattern="/login" access="ROLE_ANONYMOUS"/> <access-denied-handler error-page="/index.jsp"/> </http> 

你可以尝试检查

 if(SecurityContextHolder.getContext().getAuthentication() == null) 

True表示用户未通过身份validation,因此可以发送到login页面。 我不知道这是多么强大/可靠,但尝试似乎是合理的。