我使用Spring安全和spring oauth在我的网络应用程序中进行身份验证(使用jwt令牌)。这工作正常,令牌交换,我可以正确登录。但是,一旦登录,即使令牌执行,身份验证也不会过期。当我尝试重用令牌从资源服务器获取资源时,它返回拒绝访问,因为令牌不再有效。
我的网络应用是一个有状态(vaadin)的webapp。它使用会话来处理很多东西,我无法使用它。使用OAuth进行身份验证后,它将使用"以前经过身份验证:org.springframework.security.oauth2.provider.OAuth2Authentication"检查它是否经过验证,我认为这只是" true"直到会议被销毁。
我的休息api是状态/无会话,因此每次都会正确验证令牌。如果它过期,它将给出401。
我发现处理此问题的唯一方法是相当丑陋:如果api返回401,则会使会话无效。但我希望看到的是,Web应用程序还会检查每个请求上令牌的有效性。有没有办法在使用会话时执行此操作?
这是我的webapp的oauth安全配置部分。
@Configuration
@EnableOAuth2Sso
public class OAuthConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.logoutSuccessUrl("/")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.and()
.exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendRedirect("/login"));
}
}
答案 0 :(得分:1)
总体上,我花了一天时间研究这个问题以及OAuth2的机制。我可以确认访问令牌过期后未刷新的上述发现是正确的。在令牌过期之前或之后,对象身份验证永远不会更改。但我发现了一种无需发送401即可刷新令牌的复杂方法。 我使用了this tutorial
中的一些代码这就是方法。首先创建一个自定义过滤器:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
OAuth2ClientContext ctxt = restTemplate.getOAuth2ClientContext();
if (ctxt != null) {
OAuth2AccessToken accessToken = ctxt.getAccessToken();
if (accessToken != null && accessToken.isExpired()) {
SecurityContextHolder.getContext().setAuthentication(null);
}
}
chain.doFilter(request, response);
}
如果令牌已过期,则此过滤器会将Authentication对象设置为null。 此过滤器必须在AnonymousAuthenticationFilter之前注册:
http.addFilterBefore(customFilter, AnonymousAuthenticationFilter.class);
AnonymousAuthenticationFilter将使用“ anonymous”属性填充身份验证。然后,最后一个过滤器将抛出AccessDeniedException并开始从服务器获取令牌的过程。仅当Authentication对象使用“匿名”属性填充时,AccessDeniedException才能开始获取新令牌的过程。 服务器实际上会刷新令牌,并以新的过期时间发送回新令牌。
但是问题仍然存在,是否曾经打算以这种方式使用此属性。也许这个到期时间是从发行令牌到客户端收到令牌之间的时间?如果令牌在过期后到达,则会引发异常。也许这应该是这样工作的?
答案 1 :(得分:0)
我最终使用默认令牌代替jwt并使用auth服务器的tokencheck端点进行检查。这样就可以对每个请求进行验证。
答案 2 :(得分:0)
通过添加过滤器来解决此问题的解决方法,该过滤器检查访问令牌是否已过期并发送401响应。此响应在UI端处理,并要求用户刷新会话。刷新会话时,也会刷新访问令牌。解决方案适用于JWT。
代码:
@Component
public class ExpiredTokenFilter implements Filter {
@Autowired
private OAuth2ClientContext oAuth2ClientContext;
@Override
public void doFilter(ServletRequest servletRequest...) throws... {
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
if (accessToken != null && accessToken.isExpired()) {
HttpServletResponse httpServletReponse = (HttpServletResponse) servletResponse;
httpServletReponse.sendError(401);
return;
}
}
/* ... rest of code */
}