새 사용자가 ‘새 계정’양식을 제출 한 후 다음 페이지에서 로그인 할 필요가 없도록 해당 사용자를 수동으로 로그인하고 싶습니다.
스프링 보안 인터셉터를 통과하는 일반 양식 로그인 페이지는 잘 작동합니다.
새 계정 양식 컨트롤러에서 UsernamePasswordAuthenticationToken을 만들고 SecurityContext에서 수동으로 설정합니다.
SecurityContextHolder.getContext().setAuthentication(authentication);
같은 페이지에서 나중에 사용자가 다음으로 로그인했는지 확인합니다.
SecurityContextHolder.getContext().getAuthentication().getAuthorities();
이것은 인증에서 이전에 설정 한 권한을 반환합니다. 모든 것이 좋습니다.
그러나 내가로드 한 바로 다음 페이지에서이 동일한 코드가 호출되면 인증 토큰은 UserAnonymous입니다.
이전 요청에서 설정 한 인증을 유지하지 않은 이유가 명확하지 않습니다. 이견있는 사람?
- 세션 ID가 올바르게 설정되지 않은 것과 관련이 있습니까?
- 어떻게 든 내 인증을 덮어 쓸 수있는 것이 있습니까?
- 인증을 저장하려면 다른 단계가 필요한가요?
- 아니면 어떻게 든 단일 요청이 아닌 전체 세션에 걸쳐 인증을 선언하기 위해해야 할 일이 있습니까?
여기서 무슨 일이 일어나고 있는지 확인하는 데 도움이 될 몇 가지 생각을 찾고 있습니다.
답변
나는 당신과 얼마 전에 같은 문제를 겪었습니다. 세부 사항을 기억할 수 없지만 다음 코드는 나를 위해 일했습니다. 이 코드는 Spring Webflow 흐름 내에서 사용되므로 RequestContext 및 ExternalContext 클래스가 사용됩니다. 그러나 가장 관련성이 높은 부분은 doAutoLogin 메서드입니다.
public String registerUser(UserRegistrationFormBean userRegistrationFormBean,
RequestContext requestContext,
ExternalContext externalContext) {
try {
Locale userLocale = requestContext.getExternalContext().getLocale();
this.userService.createNewUser(userRegistrationFormBean, userLocale, Constants.SYSTEM_USER_ID);
String emailAddress = userRegistrationFormBean.getChooseEmailAddressFormBean().getEmailAddress();
String password = userRegistrationFormBean.getChoosePasswordFormBean().getPassword();
doAutoLogin(emailAddress, password, (HttpServletRequest) externalContext.getNativeRequest());
return "success";
} catch (EmailAddressNotUniqueException e) {
MessageResolver messageResolvable
= new MessageBuilder().error()
.source(UserRegistrationFormBean.PROPERTYNAME_EMAIL_ADDRESS)
.code("userRegistration.emailAddress.not.unique")
.build();
requestContext.getMessageContext().addMessage(messageResolvable);
return "error";
}
}
private void doAutoLogin(String username, String password, HttpServletRequest request) {
try {
// Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticationProvider.authenticate(token);
logger.debug("Logging in with [{}]", authentication.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
logger.error("Failure in autoLogin", e);
}
}
답변
다른 전체 솔루션을 찾을 수 없어서 게시 할 것이라고 생각했습니다. 이것은 약간의 해킹 일 수 있지만 위의 문제로 문제를 해결했습니다.
public void login(HttpServletRequest request, String userName, String password)
{
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);
// Authenticate the user
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
// Create a new session and add the security context.
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}
답변
궁극적으로 문제의 원인을 파악했습니다.
보안 컨텍스트를 수동으로 만들면 세션 개체가 만들어지지 않습니다. 요청이 처리를 완료 할 때만 Spring Security 메커니즘은 세션 객체가 null임을 인식합니다 (요청이 처리 된 후 세션에 보안 컨텍스트를 저장하려고 할 때).
요청이 끝나면 Spring Security는 새로운 세션 객체와 세션 ID를 생성합니다. 그러나이 새 세션 ID는 브라우저에 대한 응답이 이루어진 후 요청이 끝날 때 발생하기 때문에 브라우저에 전달되지 않습니다. 이로 인해 다음 요청에 이전 세션 ID가 포함되어있을 때 새 세션 ID (및 수동으로 로그온 한 사용자를 포함하는 보안 컨텍스트)가 손실됩니다.
답변
무슨 일이 일어나고 있는지 더 잘 이해하려면 디버그 로깅을 켜십시오.
HTTP 응답에 반환 된 헤더를 확인하기 위해 브라우저 측 디버거를 사용하여 세션 쿠키가 설정되고 있는지 알 수 있습니다. (다른 방법도 있습니다.)
한 가지 가능성은 SpringSecurity가 보안 세션 쿠키를 설정하고 요청 된 다음 페이지에 “https”URL 대신 “http”URL이 있다는 것입니다. (브라우저는 “http”URL에 대한 보안 쿠키를 보내지 않습니다.)
답변
Servlet 2.4의 새로운 필터링 기능은 기본적으로 필터가 애플리케이션 서버에 의한 실제 요청 처리 전후의 요청 흐름에서만 작동 할 수 있다는 제한을 완화합니다. 대신 Servlet 2.4 필터는 이제 모든 디스패치 지점에서 요청 디스패처와 상호 작용할 수 있습니다. 즉, 웹 리소스가 요청을 다른 리소스에 전달할 때 (예 : 동일한 애플리케이션의 JSP 페이지에 요청을 전달하는 서블릿) 대상 리소스가 요청을 처리하기 전에 필터가 작동 할 수 있습니다. 또한 웹 리소스에 다른 웹 리소스 (예 : 여러 다른 JSP 페이지의 출력을 포함하는 JSP 페이지)의 출력 또는 기능이 포함되는 경우 Servlet 2.4 필터가 포함 된 각 리소스의 전후에 작동 할 수 있습니다. .
해당 기능을 켜려면 다음이 필요합니다.
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/<strike>*</strike></url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
RegistrationController
return "forward:/login?j_username=" + registrationModel.getUserEmail()
+ "&j_password=" + registrationModel.getPassword();
답변
extjs 응용 프로그램을 테스트하려고 시도했지만 testingAuthenticationToken을 성공적으로 설정 한 후 갑자기 명확한 원인없이 작동이 중지되었습니다.
위의 답변을 얻을 수 없었기 때문에 내 해결책은 테스트 환경에서이 봄을 건너 뛰는 것이 었습니다. 나는 다음과 같이 봄 주위에 솔기를 도입했습니다.
public class SpringUserAccessor implements UserAccessor
{
@Override
public User getUser()
{
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
return (User) authentication.getPrincipal();
}
}
여기서 사용자는 사용자 정의 유형입니다.
그런 다음 테스트 코드를 전환하는 옵션이있는 클래스로 래핑합니다.
public class CurrentUserAccessor
{
private static UserAccessor _accessor;
public CurrentUserAccessor()
{
_accessor = new SpringUserAccessor();
}
public User getUser()
{
return _accessor.getUser();
}
public static void UseTestingAccessor(User user)
{
_accessor = new TestUserAccessor(user);
}
}
테스트 버전은 다음과 같습니다.
public class TestUserAccessor implements UserAccessor
{
private static User _user;
public TestUserAccessor(User user)
{
_user = user;
}
@Override
public User getUser()
{
return _user;
}
}
호출 코드에서 나는 여전히 데이터베이스에서로드 된 적절한 사용자를 사용하고 있습니다.
User user = (User) _userService.loadUserByUsername(username);
CurrentUserAccessor.UseTestingAccessor(user);
실제로 보안을 사용해야하는 경우에는 분명히 적합하지 않지만 테스트 배포를 위해 보안이없는 설정으로 실행 중입니다. 나는 다른 누군가가 비슷한 상황에 처할 것이라고 생각했습니다. 이것은 이전에 정적 종속성을 조롱하는 데 사용한 패턴입니다. 다른 대안은 래퍼 클래스의 정 적성을 유지할 수 있지만 필요한 경우 CurrentUserAccessor를 클래스에 전달해야하므로 코드의 종속성이 더 명시 적이므로 선호합니다.
답변
