首页 文章

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

提问于
浏览
4

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

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

3 回答

  • 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

    理解这个问题有点老了,我希望以下内容可以帮助那些寻找问题正确答案的人

    OP没有询问令牌失效,而是如何在用户身份验证成功通过后立即使Spring OSuth2服务器上的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>
    

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

    server:
      session:
        timeout: 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();
    

相关问题