如何在 Spring Security 无状态(jwt 令牌)中注销用户?
Posted
技术标签:
【中文标题】如何在 Spring Security 无状态(jwt 令牌)中注销用户?【英文标题】:How to logout user in spring security stateless (jwt token)? 【发布时间】:2019-02-02 15:18:13 【问题描述】:我在spring创建了一个微服务系统,在我的项目中我有3个前端和多个微服务,这些前端使用(并存储到cookie中)jwt令牌从我的微服务中获取资源。
我的用户登录场景: 当用户想要从前端登录时,我重定向到我的实时项目(另一个前端)用户在我的实时前端登录并重定向到前端并在查询参数中传递令牌,所以前端可以使用这个令牌来获取资源。 当用户从前端 2 登录时重定向到 live,当用户在存储的 live 令牌的 cookie 中登录并且没有获取用户名和密码时重定向到具有存在令牌的前端 2。 这是我的 sso :)。
但我无法注销用户,因为当用户注销时(从 cookie 中删除令牌)如何理解用户注销的其他前端。
我想我必须创建会话来获取用户登录状态。或者我需要集中令牌状态检查。
如何创建这个会话??
我的spring安全码是spring的默认安全码。
最后我为我的英语道歉。 ;)
我的实时项目代码 (UAA)
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter
@Value("$jwt.token.access-token-validity-seconds")
private Integer accessTokenValiditySeconds;
@Value("$jwt.token.support-refresh-token")
private Boolean supportRefreshToken;
private final AuthenticationManager authenticationManager;
private final ClientDetailsServiceImpl clientDetailsService;
private final AccountService userDetailsService;
public OAuth2AuthorizationServerConfig(AuthenticationManager authenticationManager,
ClientDetailsServiceImpl clientDetailsService, AccountService userDetailsService)
super();
this.authenticationManager = authenticationManager;
this.clientDetailsService = clientDetailsService;
this.userDetailsService = userDetailsService;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.withClientDetails(clientDetailsService);
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
@Bean
public TokenStore tokenStore()
return new JwtTokenStore(accessTokenConverter());
@Bean
public JwtAccessTokenConverter accessTokenConverter()
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("key.jks"), "sayar1234".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("key"));
Resource resource = new ClassPathResource("public-key.txt");
String publicKey = null;
try
publicKey = IOUtils.toString(resource.getInputStream());
catch (final IOException e)
throw new RuntimeException(e);
converter.setVerifierKey(publicKey);
return converter;
@Bean
@Primary
public DefaultTokenServices tokenServices()
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(supportRefreshToken);
defaultTokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
return defaultTokenServices;
@Bean
public TokenEnhancer tokenEnhancer()
return new CustomTokenEnhancer();
其他资源
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends
ResourceServerConfigurerAdapter
@Override
public void configure(final HttpSecurity http) throws Exception
// @formatter:off
http.csrf().disable() // csrf
.antMatcher("/**").authorizeRequests() // /**
.antMatchers("/actuator/**").permitAll() // Actuator
.antMatchers("/ws/**").permitAll() // websocket
.anyRequest().authenticated();
// @formatter:on
@Override
public void configure(ResourceServerSecurityConfigurer config)
config.tokenServices(tokenServices());
@Bean
public TokenStore tokenStore()
return new JwtTokenStore(accessTokenConverter());
@Bean
public JwtAccessTokenConverter accessTokenConverter()
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("public-key.txt");
String publicKey = null;
try
publicKey = IOUtils.toString(resource.getInputStream());
catch (final IOException e)
throw new RuntimeException(e);
converter.setVerifierKey(publicKey);
return converter;
@Bean
@Primary
public DefaultTokenServices tokenServices()
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
【问题讨论】:
【参考方案1】:我们正在使用 Redis 缓存/服务器来存储令牌。您可以在https://redis.io/documentation 阅读有关 Redis 的更多信息
登录后,我们使用此令牌作为键存储用户上下文,并将用户信息作为后端的值存储。您还可以设置此令牌的过期时间。
注销后,您需要从后端删除此令牌和相应的值。
现在每个经过身份验证的请求都将具有此令牌,并将对其进行验证并检查此令牌是否存在于 Redis 中。
2 种情况下会丢失令牌
-
用户已注销。
令牌已过期。
在每个有效请求之后,我们都会更新到期时间。因此,只有在系统空闲特定时间的情况下,该令牌才会过期。
这也是有益的分布式后端环境。
【讨论】:
以上是关于如何在 Spring Security 无状态(jwt 令牌)中注销用户?的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security 登录错误:HTTP 状态 404 - /j_spring_security_check
wso2 spring security saml 无状态集成
j_spring_security_check HTTP 状态 404(自定义登录)