로그인 성공 후 Spring Security가 이전 페이지로 리디렉션
이 질문이 이전에 요청 된 것을 알고 있지만 여기서 특정 문제에 직면하고 있습니다.
나는 스프링 보안 3.1.3을 사용합니다.
내 웹 응용 프로그램에 세 가지 가능한 로그인 사례가 있습니다.
- 로그인 페이지를 통해 로그인 : OK.
- 제한된 페이지를 통한 로그인 : 좋습니다.
- 제한되지 않은 페이지를 통한 로그인 : 괜찮지 않음 ... "제품"페이지는 모든 사람이 액세스 할 수 있으며 사용자가 로그인하면 댓글을 게시 할 수 있습니다. 따라서 사용자가 연결할 수 있도록 로그인 양식이 동일한 페이지에 포함됩니다.
사례 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 오류를 보냅니다.
편집하다:
Ralph 덕분에 해결책을 찾을 수있었습니다. 그래서 여기에 있습니다.
<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>
여기서 해결책은 매우 분명합니다. 로그인 페이지는 익명 사용자에게만 허용됩니다. 사용자가 연결되면 오류 처리기가 사용자를 홈 페이지로 리디렉션합니다.
몇 가지 테스트를했는데 모든 것이 잘 작동하는 것 같습니다.
로그인 후 (사용자가 리디렉션되는 URL) AuthenticationSuccessHandler
.
이 인터페이스 (이를 구현하는 구체적인 클래스 SavedRequestAwareAuthenticationSuccessHandler
)는 메서드에서 AbstractAuthenticationProcessingFilter
( UsernamePasswordAuthenticationFilter
) 와 같은 하위 클래스 중 하나에 의해 호출됩니다 successfulAuthentication
.
따라서 경우 3에서 다른 리디렉션을 사용하려면 하위 클래스 SavedRequestAwareAuthenticationSuccessHandler
를 지정하고 원하는 작업을 수행해야합니다.
때로는 (정확한 사용 사례에 따라) (의 수퍼 클래스 )에 의해 호출되는 useReferer
플래그 를 활성화하는 것으로 충분합니다 .AbstractAuthenticationTargetUrlRequestHandler
SimpleUrlAuthenticationSuccessHandler
SavedRequestAwareAuthenticationSuccessHandler
<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 의 좋은 대답 을 확장하고 싶습니다 . 그의 접근 방식은 좋습니다. 로그인 페이지 컨트롤러는 다음과 같아야 참조 자 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);
}
}
}
그런 다음 스프링 구성에서이 사용자 정의 클래스를 빈으로 정의하고 보안 구성에서 사용해야합니다. 당신이 사용하는 경우 주석 설정을 , 그것은 (클래스는 당신이에서 확장과 같아야합니다 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
;
}
나는 다음과 같은 해결책을 가지고 있으며 그것은 나를 위해 일했습니다.
로그인 페이지가 요청 될 때마다 세션에 참조 자 값을 씁니다.
@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);
}
맞춤 OAuth2 인증이 있으며 request.getHeader("Referer")
결정 시점에 사용할 수 없습니다. 하지만 보안 요청은 이미 저장되어 있습니다 ExceptionTranslationFilter.handleSpringSecurityException
.
protected void sendStartAuthentication(HttpServletRequest request,...
...
requestCache.saveRequest(request, response);
그래서 우리에게 필요한 것은 requestCache
Spring bean으로 공유 하는 것입니다.
@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());
}
다음 일반 솔루션은 일반 로그인, Spring Social 로그인 또는 대부분의 다른 Spring Security 필터와 함께 사용할 수 있습니다.
Spring MVC 컨트롤러에서 제품 페이지를로드 할 때 사용자가 로그인하지 않은 경우 세션의 제품 페이지 경로를 저장합니다. XML 구성에서 기본 대상 URL을 설정합니다. 예를 들면 :
Spring MVC 컨트롤러에서 리디렉션 메서드는 세션에서 경로를 읽고 redirect:<my_saved_product_path>
.
따라서 사용자가 로그인 한 후 /redirect
페이지 로 전송되어 마지막으로 방문한 제품 페이지로 즉시 리디렉션됩니다.
성공적으로 로그인 한 후 이전 페이지로 돌아 가면 다음과 같은 사용자 정의 인증 관리자를 사용할 수 있습니다.
<!-- 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;
}
}
할당 된 역할에 따라 로그인 할 때 사용자를 다른 URL로 리디렉션하기 위해 SimpleUrlAuthenticationSuccessHandler를 확장하는 Custom SuccessHandler를 사용할 수 있습니다.
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 클래스를 확장하고 사용자가 정의한 determineTargetUrl () 메소드가 반환 한 URL로 구성된 RedirectStrategy [이 경우 기본값]를 사용하여 단순히 리디렉션을 호출하는 handle () 메소드를 재정의합니다. 이 방법은 인증 개체에서 현재 로그인 한 사용자의 역할을 추출한 다음 해당 역할에 따라 적절한 URL을 구성합니다. 마지막으로 Spring Security 프레임 워크 내의 모든 리디렉션을 담당하는 RedirectStrategy는 요청을 지정된 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 / AcademicStaff]에 따라 [학생 / 관리자 / 직원에게] 사용자를 리디렉션합니다.
내가 발견 Utku하기 Ozdemir의 솔루션 어느 정도 일을하지만, 종류 패배의 세션 속성 이후 구원 요청의 목적은보다 우선합니다. 이는 보안 페이지로의 리디렉션이 의도 한대로 작동하지 않음을 의미합니다. 로그인하면 리디렉션 대상 대신에 있던 페이지로 이동하게됩니다. 따라서 대안으로 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!
}
'IT이야기' 카테고리의 다른 글
단위 테스트를 테스트하는 클래스의 친구로 만드는 것이 잘못된 이유 (0) | 2021.05.03 |
---|---|
virtualenv는 어떻게 작동합니까? (0) | 2021.05.03 |
패키지 내역을 보는 방법 (0) | 2021.05.03 |
SSL 핸드 셰이크 디버깅 (0) | 2021.05.02 |
프로그램 종료 중 Python에서 KeyboardInterrupt 잡기 (0) | 2021.05.02 |