我知道之前已经问过这个问题,但是我在这里遇到了一个特殊的问题。
我使用spring security 3.1.3。
我的网络应用程序中有3个可能的登录案例:
案例3的问题是我无法将用户重定向到“产品”页面。无论如何,他们都会在成功登录后重定向到主页。
请注意,对于案例2),成功登录后,重定向到受限制页面的工作开箱即用。
这是我的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>
我怀疑“每个页面的身份验证策略”都要对此问题负责。但是,如果我将其删除,则无法再登录... j_spring_security_check发送404错误。
编辑:
感谢拉尔夫,我找到了解决方案。所以这就是:我使用了属性
<property name="useReferer" value="true"/>
拉尔夫给我看了。之后我遇到了我的案例问题1):当通过登录页面登录时,用户停留在同一页面(并没有重定向到主页,就像以前一样)。直到这个阶段的代码如下:
<!-- 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>
至少在理论上这应该有效,但事实并非如此。我仍然不知道为什么,所以如果有人对此有答案,我很乐意创建一个新话题,让他分享他的解决方案。
与此同时,我找到了解决方法。不是最好的解决方案,但就像我说的,如果有人有更好的表现,我会全力以赴。这是登录页面的新身份验证策略:
<http use-expressions="true" auto-config="true" pattern="/login/**" >
<intercept-url pattern="/**" access="isAnonymous()" />
<access-denied-handler error-page="/"/>
</http>
此处的解决方案非常明显:只允许匿名用户使用登录页面。连接用户后,错误处理程序会将其重定向到主页。
我做了一些测试,一切似乎都很好。
答案 0 :(得分:42)
登录后会发生什么(用户被重定向到哪个网址)由AuthenticationSuccessHandler
处理。
此接口(实现它的具体类是SavedRequestAwareAuthenticationSuccessHandler
)由方法AbstractAuthenticationProcessingFilter
中的UsernamePasswordAuthenticationFilter
或其子类之一(successfulAuthentication
)调用。 / p>
因此,为了在案例3中进行其他重定向,您必须继承SavedRequestAwareAuthenticationSuccessHandler
并使其成为您想要的。
有时(取决于您的确切用例),只需启用由useReferer
(超级AbstractAuthenticationTargetUrlRequestHandler
)调用的SimpleUrlAuthenticationSuccessHandler
SavedRequestAwareAuthenticationSuccessHandler
标志即可。< / p>
<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>
答案 1 :(得分:35)
我想扩展 Olcay 的好答案。他的方法很好,您的登录页面控制器应该像这样将引用者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);
}
}
}
然后,在spring配置中,您应该将此自定义类定义为bean并在安全配置中使用它。如果您使用注释配置,它应该如下所示(您从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
;
}
答案 2 :(得分:5)
我有以下解决方案,它对我有用。
每当请求登录页面时,将referer值写入会话:
@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";
}
然后,在成功登录后,SavedRequestAwareAuthenticationSuccessHandler
的自定义实施会将用户重定向到上一页:
HttpSession session = request.getSession(false);
if (session != null) {
url = (String) request.getSession().getAttribute("url_prior_login");
}
重定向用户:
if (url != null) {
response.sendRedirect(url);
}
答案 3 :(得分:0)
以下通用解决方案可用于常规登录,Spring Social登录或大多数其他Spring Security过滤器。
在Spring MVC控制器中,加载产品页面时,如果用户尚未登录,请在会话中保存产品页面的路径。在XML config中,设置默认目标URL。例如:
在Spring MVC控制器中,重定向方法应该从会话中读出路径并返回redirect:<my_saved_product_path>
。
因此,在用户登录后,他们会被发送到/redirect
页面,这会立即将他们重定向回他们上次访问的产品页面。
答案 4 :(得分:0)
成功登录后返回上一页,我们可以使用以下自定义身份验证管理器:
<!-- 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 Class
@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;
}
}
答案 5 :(得分:0)
您可以使用自定义SuccessHandler扩展SimpleUrlAuthenticationSuccessHandler,以便在根据分配的角色登录时将用户重定向到不同的URL。
CustomSuccessHandler类提供自定义重定向功能:
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()方法,该方法只使用配置的RedirectStrategy [本例中为默认]使用用户定义的determineTargetUrl()方法返回的URL调用重定向。此方法从Authentication对象中提取当前登录用户的角色,然后根据角色构造适当的URL。最后,RedirectStrategy负责Spring Security框架内的所有重定向,将请求重定向到指定的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是负责基于任何自定义逻辑的最终重定向的类,在这种情况下,将根据他的角色[USER / Student / SystemAdmin / UniversityAdmin / ExaminationsStaff /重定向用户[到student / admin / staff] AcademicStaff]。
答案 6 :(得分:0)
我发现Utku Özdemir's solution在某种程度上有效,但是由于会话属性优先于它,因此会破坏保存请求的目的。这意味着重定向到安全页面将无法按预期工作 - 登录后您将被发送到您所在的页面而不是重定向目标。因此,作为替代方案,您可以使用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);
}
}
此外,您不希望在身份验证失败时保存引荐来源,因为引用者将成为登录页面本身。因此,请手动检查错误参数或提供单独的RequestMapping,如下所示。
@RequestMapping(value = "/login", params = "error")
public String loginError() {
// Don't save referrer here!
}
答案 7 :(得分:0)
我具有自定义的OAuth2授权,request.getHeader("Referer")
在决定时不可用。但是安全请求已保存在ExceptionTranslationFilter.handleSpringSecurityException
中:
protected void sendStartAuthentication(HttpServletRequest request,...
...
requestCache.saveRequest(request, response);
因此,我们所需要的只是与Spring bean共享requestCache
:
@Bean
public RequestCache requestCache() {
return new HttpSessionRequestCache();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.requestCache().requestCache(requestCache()).and()
...
}
并在授权完成后使用它:
@Autowired
private RequestCache requestCache;
public void authenticate(HttpServletRequest req, HttpServletResponse resp){
....
SavedRequest savedRequest = requestCache.getRequest(req, resp);
resp.sendRedirect(savedRequest == null ? "defaultURL" : savedRequest.getRedirectUrl());
}
答案 8 :(得分:0)
为了重定向到特定页面,无论用户角色是什么,都可以简单地使用 Spring的配置文件中的defaultSucessUrl。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.and()
.formLogin() .loginPage("/login")
.defaultSuccessUrl("/admin",true)
.loginProcessingUrl("/authenticateTheUser")
.permitAll();