Spring Security在成功login后redirect到上一页
我知道这个问题之前已经被问过了,但是我在这里面临着一个特殊的问题。
我使用弹簧安全3.1.3。
我的Web应用程序中有3个可能的login情况:
- 通过login页面login:OK。
- 通过受限页面login:也可以。
- 通过非限制性页面login:不行…每个人都可以访问“产品”页面,如果用户login,用户可以发表评论。 因此,login表单被包含在同一页面中以允许用户连接。
情况3)的问题是我无法将用户redirect到“产品”页面。 无论如何,他们会在成功login后redirect到主页。
请注意,对于情况2)成功login后,redirect到受限制页面的方式是开箱即用的。
这是我的security.xml文件的相关部分:
<!-- Authentication policy for the restricted page --> <http use-expressions="true" auto-config="true" pattern="/restrictedPage/**"> <form-login login-page="/login/restrictedLogin" authentication-failure-handler-ref="authenticationFailureHandler" /> <intercept-url pattern="/**" access="isAuthenticated()" /> </http> <!-- Authentication policy for every page --> <http use-expressions="true" auto-config="true"> <form-login login-page="/login" authentication-failure-handler-ref="authenticationFailureHandler" /> <logout logout-url="/logout" logout-success-url="/" /> </http>
我怀疑“每个页面的身份validation策略”是对这个问题负责。 但是,如果我删除它,我不能再login… j_spring_security_check发送404错误。
编辑:
感谢拉尔夫,我能find一个解决scheme。 所以这是事情:我使用的财产
<property name="useReferer" value="true"/>
拉尔夫给我看。 之后,我遇到了一个问题1):通过login页面进行login时,用户停留在同一页面(而不是redirect到主页面,就像以前一样)。 直到这个阶段的代码如下:
<!-- Authentication policy for login page --> <http use-expressions="true" auto-config="true" pattern="/login/**"> <form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandlerWithoutReferer" /> </http> <!-- Authentication policy for every page --> <http use-expressions="true" auto-config="true"> <form-login login-page="/login" authentication-failure-handler-ref="authenticationFailureHandler" /> <logout logout-url="/logout" logout-success-url="/" authentication-success-handler-ref="authenticationSuccessHandler"/> </http> <beans:bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <!-- After login, return to the last visited page --> <beans:property name="useReferer" value="true" /> </beans:bean> <beans:bean id="authenticationSuccessHandlerWithoutReferer" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <!-- After login, stay to the same page --> <beans:property name="useReferer" value="false" /> </beans:bean>
至less在理论上这应该起作用,但事实并非如此。 我仍然不知道为什么,所以如果有人有这个答案,我会很乐意创造一个新的话题,让他分享他的解决scheme。
与此同时,我来了一个解决方法。 不是最好的解决办法,但正如我所说,如果有人有更好的展示,我全部耳朵。 所以这是login页面的新authentication策略:
<http use-expressions="true" auto-config="true" pattern="/login/**" > <intercept-url pattern="/**" access="isAnonymous()" /> <access-denied-handler error-page="/"/> </http>
这里的解决scheme非常明显:login页面只允许匿名用户使用。 一旦用户连接,error handling程序将他redirect到主页。
我做了一些testing,一切似乎都很好。
login后(用户redirect到哪个url)会发生什么情况由AuthenticationSuccessHandler
处理。
这个接口(一个实现它的具体类是SavedRequestAwareAuthenticationSuccessHandler
)被AbstractAuthenticationProcessingFilter
或者它的一个子类调用,例如方法successfulAuthentication
中的( UsernamePasswordAuthenticationFilter
)。
所以为了在SavedRequestAwareAuthenticationSuccessHandler
情况下有一个其他的redirect,你必须SavedRequestAwareAuthenticationSuccessHandler
子类SavedRequestAwareAuthenticationSuccessHandler
并使它做你想做的事情。
有时候(取决于你的确切用例)足够启用由SimpleUrlAuthenticationSuccessHandler
( SavedRequestAwareAuthenticationSuccessHandler
超类)调用的AbstractAuthenticationTargetUrlRequestHandler
的useReferer
标志。
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <property name="filterProcessesUrl" value="/login/j_spring_security_check" /> <property name="authenticationManager" ref="authenticationManager" /> <property name="authenticationSuccessHandler"> <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <property name="useReferer" value="true"/> </bean> </property> <property name="authenticationFailureHandler"> <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <property name="defaultFailureUrl" value="/login?login_error=t" /> </bean> </property> </bean>
我想扩展Olcay的很好的答案。 他的方法是好的,你的login页面控制器应该是这样的推荐人url进入会议:
@RequestMapping(value = "/login", method = RequestMethod.GET) public String loginPage(HttpServletRequest request, Model model) { String referrer = request.getHeader("Referer"); request.getSession().setAttribute("url_prior_login", referrer); // some other stuff return "login"; }
你应该扩展SavedRequestAwareAuthenticationSuccessHandler
并重写它的onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
方法。 像这样的东西:
public class MyCustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public MyCustomLoginSuccessHandler(String defaultTargetUrl) { setDefaultTargetUrl(defaultTargetUrl); } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { HttpSession session = request.getSession(); if (session != null) { String redirectUrl = (String) session.getAttribute("url_prior_login"); if (redirectUrl != null) { // we do not forget to clean this attribute from session session.removeAttribute("url_prior_login"); // then we redirect getRedirectStrategy().sendRedirect(request, response, redirectUrl); } else { super.onAuthenticationSuccess(request, response, authentication); } } else { super.onAuthenticationSuccess(request, response, authentication); } } }
然后,在您的springconfiguration中,您应该将此自定义类定义为一个bean,并将其用于您的安全性configuration。 如果您使用注释configuration ,它应该看起来像这样(您从WebSecurityConfigurerAdapter
扩展的类):
@Bean public AuthenticationSuccessHandler successHandler() { return new MyCustomLoginSuccessHandler("/yourdefaultsuccessurl"); }
在configure
方法中:
@Override protected void configure(HttpSecurity http) throws Exception { http // bla bla .formLogin() .loginPage("/login") .usernameParameter("username") .passwordParameter("password") .successHandler(successHandler()) .permitAll() // etc etc ; }
我有以下的解决scheme,它为我工作。
每当请求login页面时,将引用值写入会话:
@RequestMapping(value="/login", method = RequestMethod.GET) public String login(ModelMap model,HttpServletRequest request) { String referrer = request.getHeader("Referer"); if(referrer!=null){ request.getSession().setAttribute("url_prior_login", referrer); } return "user/login"; }
然后,成功login后, SavedRequestAwareAuthenticationSuccessHandler
自定义实现将用户redirect到上一页:
HttpSession session = request.getSession(false); if (session != null) { url = (String) request.getSession().getAttribute("url_prior_login"); }
redirect用户:
if (url != null) { response.sendRedirect(url); }
以下通用解决scheme可以用于常规login,Spring社交login或大多数其他Spring Securityfilter。
在您的Spring MVC控制器中,加载产品页面时,如果用户尚未login,请将会话path保存到会话中。在XMLconfiguration中,设置默认目标URL。 例如:
在你的Spring MVC控制器中,redirect方法应该从会话中读出path并返回redirect:<my_saved_product_path>
。
所以,用户login后,他们将被发送到/redirect
页面,这将立即将他们redirect到他们上次访问的产品页面。
在成功login后返回上一页,我们可以使用下面的自定义身份validationpipe理器,如下所示:
<!-- enable use-expressions --> <http auto-config="true" use-expressions="true"> <!-- src** matches: src/bar.c src/baz.c src/test/bartest.c--> <intercept-url pattern="/problemSolution/home/**" access="hasRole('ROLE_ADMIN')"/> <intercept-url pattern="favicon.ico" access="permitAll"/> <form-login authentication-success-handler-ref="authenticationSuccessHandler" always-use-default-target="true" login-processing-url="/checkUser" login-page="/problemSolution/index" default-target-url="/problemSolution/home" authentication-failure-url="/problemSolution/index?error" username-parameter="username" password-parameter="password"/> <logout logout-url="/problemSolution/logout" logout-success-url="/problemSolution/index?logout"/> <!-- enable csrf protection --> <csrf/> </http> <beans:bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <beans:property name="defaultTargetUrl" value="/problemSolution/home"/> </beans:bean> <!-- Select users and user_roles from database --> <authentication-manager> <authentication-provider user-service-ref="customUserDetailsService"> <password-encoder hash="plaintext"> </password-encoder> </authentication-provider> </authentication-manager>
CustomUserDetailsService类
@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserService userService; public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { com.codesenior.telif.local.model.User domainUser = userService.getUser(userName); boolean enabled = true; boolean accountNonExpired = true; boolean credentialsNonExpired = true; boolean accountNonLocked = true; return new User( domainUser.getUsername(), domainUser.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, getAuthorities(domainUser.getUserRoleList()) ); } public Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoleList) { return getGrantedAuthorities(getRoles(userRoleList)); } public List<String> getRoles(List<UserRole> userRoleList) { List<String> roles = new ArrayList<String>(); for(UserRole userRole:userRoleList){ roles.add(userRole.getRole()); } return roles; } public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) { List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for (String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return authorities; } }
用户类
import com.codesenior.telif.local.model.UserRole; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserService userService; public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { com.codesenior.telif.local.model.User domainUser = userService.getUser(userName); boolean enabled = true; boolean accountNonExpired = true; boolean credentialsNonExpired = true; boolean accountNonLocked = true; return new User( domainUser.getUsername(), domainUser.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, getAuthorities(domainUser.getUserRoleList()) ); } public Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoleList) { return getGrantedAuthorities(getRoles(userRoleList)); } public List<String> getRoles(List<UserRole> userRoleList) { List<String> roles = new ArrayList<String>(); for(UserRole userRole:userRoleList){ roles.add(userRole.getRole()); } return roles; } public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) { List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for (String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return authorities; } }
UserRole类
@Entity public class UserRole { @Id @GeneratedValue private Integer userRoleId; private String role; @ManyToMany(fetch = FetchType.LAZY, mappedBy = "userRoleList") @JsonIgnore private List<User> userList; public Integer getUserRoleId() { return userRoleId; } public void setUserRoleId(Integer userRoleId) { this.userRoleId= userRoleId; } public String getRole() { return role; } public void setRole(String role) { this.role= role; } @Override public String toString() { return String.valueOf(userRoleId); } public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList= userList; } }
您可以使用Custom SuccessHandler扩展SimpleUrlAuthenticationSuccessHandler,以根据其分配的angular色login时将用户redirect到不同的URL。
CustomSuccessHandler类提供自定义redirectfunction:
package com.mycompany.uomrmsweb.configuration; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; @Component public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{ private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Override protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { String targetUrl = determineTargetUrl(authentication); if (response.isCommitted()) { System.out.println("Can't redirect"); return; } redirectStrategy.sendRedirect(request, response, targetUrl); } protected String determineTargetUrl(Authentication authentication) { String url=""; Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); List<String> roles = new ArrayList<String>(); for (GrantedAuthority a : authorities) { roles.add(a.getAuthority()); } if (isStaff(roles)) { url = "/staff"; } else if (isAdmin(roles)) { url = "/admin"; } else if (isStudent(roles)) { url = "/student"; }else if (isUser(roles)) { url = "/home"; } else { url="/Access_Denied"; } return url; } public void setRedirectStrategy(RedirectStrategy redirectStrategy) { this.redirectStrategy = redirectStrategy; } protected RedirectStrategy getRedirectStrategy() { return redirectStrategy; } private boolean isUser(List<String> roles) { if (roles.contains("ROLE_USER")) { return true; } return false; } private boolean isStudent(List<String> roles) { if (roles.contains("ROLE_Student")) { return true; } return false; } private boolean isAdmin(List<String> roles) { if (roles.contains("ROLE_SystemAdmin") || roles.contains("ROLE_ExaminationsStaff")) { return true; } return false; } private boolean isStaff(List<String> roles) { if (roles.contains("ROLE_AcademicStaff") || roles.contains("ROLE_UniversityAdmin")) { return true; } return false; } }
扩展Spring SimpleUrlAuthenticationSuccessHandler类和重写handle()方法,该方法只需使用configuration的RedirectStrategy [默认情况下]用用户定义的determineTargetUrl()方法返回的URL调用redirect。 此方法从authentication对象中提取当前login用户的angular色,然后根据angular色构build适当的URL。 最后,RedirectStrategy负责Spring Security框架内的所有redirect,将请求redirect到指定的URL。
使用SecurityConfiguration类注册CustomSuccessHandler:
package com.mycompany.uomrmsweb.configuration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired @Qualifier("customUserDetailsService") UserDetailsService userDetailsService; @Autowired CustomSuccessHandler customSuccessHandler; @Autowired public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/home").access("hasRole('USER')") .antMatchers("/admin/**").access("hasRole('SystemAdmin') or hasRole('ExaminationsStaff')") .antMatchers("/staff/**").access("hasRole('AcademicStaff') or hasRole('UniversityAdmin')") .antMatchers("/student/**").access("hasRole('Student')") .and().formLogin().loginPage("/login").successHandler(customSuccessHandler) .usernameParameter("username").passwordParameter("password") .and().csrf() .and().exceptionHandling().accessDeniedPage("/Access_Denied"); } }
successHandler是负责基于任何自定义逻辑的最终redirect的类,在这种情况下,根据他的angular色[USER / Student / SystemAdmin / UniversityAdmin / ExaminationsStaff / AcademicStaff]来redirect用户[给学生/pipe理员/员工]。
我发现UtkuÖzdemir的解决scheme在某种程度上起作用,但是由于会话属性将优先于保存的请求的目的, 这意味着redirect到安全页面将无法按预期工作 – login后,您将被发送到您所在的页面,而不是redirect目标。 所以作为替代scheme,您可以使用SavedRequestAwareAuthenticationSuccessHandler的修改版本,而不是扩展它。 这将允许您更好地控制何时使用会话属性。
这里是一个例子:
private static class MyCustomLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private RequestCache requestCache = new HttpSessionRequestCache(); @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest == null) { HttpSession session = request.getSession(); if (session != null) { String redirectUrl = (String) session.getAttribute("url_prior_login"); if (redirectUrl != null) { session.removeAttribute("url_prior_login"); getRedirectStrategy().sendRedirect(request, response, redirectUrl); } else { super.onAuthenticationSuccess(request, response, authentication); } } else { super.onAuthenticationSuccess(request, response, authentication); } return; } String targetUrlParameter = getTargetUrlParameter(); if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) { requestCache.removeRequest(request, response); super.onAuthenticationSuccess(request, response, authentication); return; } clearAuthenticationAttributes(request); // Use the DefaultSavedRequest URL String targetUrl = savedRequest.getRedirectUrl(); logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl); getRedirectStrategy().sendRedirect(request, response, targetUrl); } }
另外,当authentication失败时,您不希望保存引用者,因为引用者将成为login页面本身。 因此,手动检查错误参数或提供一个单独的RequestMapping,如下所示。
@RequestMapping(value = "/login", params = "error") public String loginError() { // Don't save referrer here! }
- 如何触发基于服务器响应的jquery.ajax()错误callback,而不是HTTP 500?
- Spring启动@ResponseBody不会序列化实体ID
- JPA @OneToMany – >父 – 子参考(外键)
- 如何将Spring与Hibernate会话和事务pipe理集成?
- 使用Spring和Hibernate跨多个数据库进行分布式事务的“最佳”方式是什么?
- 您在Spring MVC应用程序中使用什么命名约定?
- Spring Boot – 处理Hibernate SessionFactory
- Spring注释@Controller是否与@Service相同?
- 我怎样才能有所有用户login(通过弹簧安全)我的Web应用程序的列表