我们使用spring security OAuth2来保护REST服务。应用程序可以调用/oauth/authorize
,/oauth/token
或/rest-api
个端点。 token和rest-api端点是无状态的,不需要会话。
用户通过身份验证后,我们可以使会话无效吗?如果是这样,最好的方法是什么。我们希望用户在每次拨打/oauth/authorize
时都能登录。目前,只要会话存在,对/oauth/authorize
的调用就会跳过身份验证。
答案 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())
// ...
;
}
}