Programing

Spring Security / SpringMVC에서 인증 된 사용자를 수동으로 설정하는 방법

lottogame 2020. 8. 16. 21:49
반응형

Spring Security / SpringMVC에서 인증 된 사용자를 수동으로 설정하는 방법


새 사용자가 '새 계정'양식을 제출 한 후 다음 페이지에서 로그인 할 필요가 없도록 해당 사용자를 수동으로 로그인하고 싶습니다.

스프링 보안 인터셉터를 통과하는 일반 양식 로그인 페이지는 잘 작동합니다.

새 계정 양식 컨트롤러에서 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);
    }

}

I couldn't find any other full solutions so I thought I would post mine. This may be a bit of a hack, but it resolved the issue to the above problem:

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);
}

Ultimately figured out the root of the problem.

When I create the security context manually no session object is created. Only when the request finishes processing does the Spring Security mechanism realize that the session object is null (when it tries to store the security context to the session after the request has been processed).

At the end of the request Spring Security creates a new session object and session ID. However this new session ID never makes it to the browser because it occurs at the end of the request, after the response to the browser has been made. This causes the new session ID (and hence the Security context containing my manually logged on user) to be lost when the next request contains the previous session ID.


Turn on debug logging to get a better picture of what is going on.

You can tell if the session cookies are being set by using a browser-side debugger to look at the headers returned in HTTP responses. (There are other ways too.)

One possibility is that SpringSecurity is setting secure session cookies, and your next page requested has an "http" URL instead of an "https" URL. (The browser won't send a secure cookie for an "http" URL.)


The new filtering feature in Servlet 2.4 basically alleviates the restriction that filters can only operate in the request flow before and after the actual request processing by the application server. Instead, Servlet 2.4 filters can now interact with the request dispatcher at every dispatch point. This means that when a Web resource forwards a request to another resource (for instance, a servlet forwarding the request to a JSP page in the same application), a filter can be operating before the request is handled by the targeted resource. It also means that should a Web resource include the output or function from other Web resources (for instance, a JSP page including the output from multiple other JSP pages), Servlet 2.4 filters can work before and after each of the included resources. .

To turn on that feature you need:

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();

I was trying to test an extjs application and after sucessfully setting a testingAuthenticationToken this suddenly stopped working with no obvious cause.

I couldn't get the above answers to work so my solution was to skip out this bit of spring in the test environment. I introduced a seam around spring like this:

public class SpringUserAccessor implements UserAccessor
{
    @Override
    public User getUser()
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        return (User) authentication.getPrincipal();
    }
}

User is a custom type here.

I'm then wrapping it in a class which just has an option for the test code to switch spring out.

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);
    }
}

The test version just looks like this:

public class TestUserAccessor implements UserAccessor
{
    private static User _user;

    public TestUserAccessor(User user)
    {
        _user = user;
    }

    @Override
    public User getUser()
    {
        return _user;
    }
}

In the calling code I'm still using a proper user loaded from the database:

    User user = (User) _userService.loadUserByUsername(username);
    CurrentUserAccessor.UseTestingAccessor(user);

Obviously this wont be suitable if you actually need to use the security but I'm running with a no-security setup for the testing deployment. I thought someone else might run into a similar situation. This is a pattern I've used for mocking out static dependencies before. The other alternative is you can maintain the staticness of the wrapper class but I prefer this one as the dependencies of the code are more explicit since you have to pass CurrentUserAccessor into classes where it is required.

참고URL : https://stackoverflow.com/questions/4664893/how-to-manually-set-an-authenticated-user-in-spring-security-springmvc

반응형