如果用户在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页面。 我不知道这是多么强大/可靠,但尝试似乎是合理的。