Spring安全性OAuth2 - 在身份验证后使会话无效

时间:2015-10-08 03:42:30

标签: spring-security spring-security-oauth2

我们使用spring security OAuth2来保护REST服务。应用程序可以调用/oauth/authorize/oauth/token/rest-api个端点。 token和rest-api端点是无状态的,不需要会话。

用户通过身份验证后,我们可以使会话无效吗?如果是这样,最好的方法是什么。我们希望用户在每次拨打/oauth/authorize时都能登录。目前,只要会话存在,对/oauth/authorize的调用就会跳过身份验证。

4 个答案:

答案 0 :(得分:3)

了解问题有点陈旧,我希望以下内容对那些为问题寻找正确答案的人有所帮助

OP没有询问令牌失效,而是在用户身份验证成功通过后如何使Spring OAuth2服务器上的httpSession无效,并且有效的access_token或authorization_code(后续获取access_token)返回给客户端。

此用例还没有开箱即用的解决方案。但可以找到最活跃的spring-security-oauth撰稿人Dave Syer的工作解决方法here on GitHub

从那里复制代码:

@Service
@Aspect
public class SessionInvalidationOauth2GrantAspect {

    private static final String FORWARD_OAUTH_CONFIRM_ACCESS = "forward:/oauth/confirm_access";
    private static final Logger logger = Logger.getLogger(SessionInvalidationOauth2GrantAspect.class);

    @AfterReturning(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && @annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
    public void authorizationAdvice(JoinPoint joinpoint, ModelAndView result) throws Throwable {

        // If we're not going to the confirm_access page, it means approval has been skipped due to existing access
        // token or something else and they'll be being sent back to app. Time to end session.
        if (!FORWARD_OAUTH_CONFIRM_ACCESS.equals(result.getViewName())) {
            invalidateSession();
        }
    }

    @AfterReturning(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && @annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
    public void authorizationAdvice(JoinPoint joinpoint, View result) throws Throwable {
        // Anything returning a view and not a ModelView is going to be redirecting outside of the app (I think). 
        // This happens after the authorize approve / deny page with the POST to /oauth/authorize. This is the time
        // to kill the session since they'll be being sent back to the requesting app.
        invalidateSession();
    }

    @AfterThrowing(value = "within(org.springframework.security.oauth2.provider.endpoint..*) &&  @annotation(org.springframework.web.bind.annotation.RequestMapping)", throwing = "error")
    public void authorizationErrorAdvice(JoinPoint joinpoint) throws Throwable {
        invalidateSession();
    }

    private void invalidateSession() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        HttpSession session = request.getSession(false);
        if (session != null) {
            logger.warn(String.format("As part of OAuth application grant processing, invalidating session for request %s", request.getRequestURI()));

            session.invalidate();
            SecurityContextHolder.clearContext();
        }
    }

}

添加pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

另一个解决方案可能是将会话时间设置为某个非常小的值。实现这一目标的最简单方法是将以下内容放到application.yml config:

server:
  session:
    timeout: 1

但这不是理想的解决方案,因为最小值可能是提供者是1(零是为无限会话保留的),它是几分钟而不是几秒钟

答案 1 :(得分:2)

首先:在您的配置中声明bean,其中包含oauth的标记存储

@Bean
@Primary
public TokenStore tokenStore() {
    return new InMemoryTokenStore();
}

对于控制器方法,我们制作了以下类

@Controller
 public class TokenController {

    @RequestMapping(value = "/oauth/token/revoke", method = RequestMethod.POST)
    public @ResponseBody void create(@RequestParam("token") String value) {
        this.revokeToken(value);
    }

    @Autowired
    TokenStore tokenStore;

    public boolean revokeToken(String tokenValue) {
        OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
        if (accessToken == null) {
            return false;
        }
        if (accessToken.getRefreshToken() != null) {
            tokenStore.removeRefreshToken(accessToken.getRefreshToken());
        }
        tokenStore.removeAccessToken(accessToken);
        return true;
    }
}

如果您不想使用此方法,您可以获取当前用户的令牌自动装配Principal

    OAuth2Authentication authorization = (OAuth2Authentication) principal;
    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authorization.getDetails();
    String token = details.getTokenValue();

甚至自动装配OAuth2Authentication

    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
    String token = details.getTokenValue();

答案 2 :(得分:1)

据我所知,您正在尝试以某种方式执行某些操作后以编程方式注销。您可能应该查看SecurityContextLogoutHandler并查看其工作原理。有一种注销方法。我认为将其作为建议将解决您的问题。

public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
      Assert.notNull(request, "HttpServletRequest required");
      if (invalidateHttpSession) {
          HttpSession session = request.getSession(false);
          if (session != null) {
              session.invalidate();
          }
      }

      SecurityContextHolder.clearContext();
  }

答案 3 :(得分:0)

我可以提供这样的选择(根据@de_xtr推荐):

import static org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes;

@Slf4j
@Component
@Aspect
public class InvalidateSessionAspect {

    private final LogoutHandler logoutHandler;

    public InvalidateSessionAspect() {
        logoutHandler = new SecurityContextLogoutHandler();
    }

    @Pointcut("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
    public void postAccessTokenPointcut() {
    }

    @AfterReturning(value = "postAccessTokenPointcut()", returning = "entity")
    public void invalidateSession(JoinPoint jp, Object entity) {
        log.debug("[d] Trying to invalidate the session...");
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) currentRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        logoutHandler.logout(request, null, null);
        log.debug("[d] Session has been invalidated");
    }
}

该选项没有任何方面:

@Slf4j
class LogoutHandlerInterceptor implements HandlerInterceptor {
    @Override
    public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object h, ModelAndView view) {
        HttpSession session = req.getSession(false);
        if (session != null) {
            log.debug("[d] Trying to invalidate the session...");
            session.invalidate();
            SecurityContext context = SecurityContextHolder.getContext();
            context.setAuthentication(null);
            SecurityContextHolder.clearContext();
            log.debug("[d] Session has been invalidated");
        }
    }
}
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    //...

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.addInterceptor(new LogoutHandlerInterceptor())
                // ... 
                ;
    }
}