⒈JWT?
JWT(Json Web Token),是Json的一个开放的Token标准。
1,自包含,SpringSecurityOAuth的默认Token是UUID的一个随机的无意义的字符串,并不包含任何信息,信息是被单独存放的,我们还需要通过这个令牌从单独存放信息的存储那里获取信息,所以说SpringSecurityOAuth的默认Token比较依赖于存储,一旦存储信息的存储那里出现了故障,那么这个令牌就毫无用处了。而JWT Token中是包含有意义的信息的,当我们的系统拿到这个令牌以后,我们可以直接解析这个令牌来获取有用的信息。
2,密签,我们可以对发出去的令牌进行签名(指令牌被人篡改我们可以获知)
3,可扩展,Token中的信息我们可以根据业务需求进行扩展。
⒉如何在SpringSecurityOAuth中使用JWT Token替换掉默认的UUID Token
1.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2.
package cn.coreqi.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; @Configuration
public class TokenStoreConfig { @Autowired
private RedisConnectionFactory redisConnectionFactory; /**
* TokenStore 负责令牌的存取
* @return
*/
// @Bean
// public TokenStore redisTokenStore(){
// return new RedisTokenStore(redisConnectionFactory);
// } @Configuration
public static class JwtTokenConfig {
/**
* Token生成中的处理
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("fanqi"); //Token签名用的密钥
//发出去的令牌需要密钥签名,验令牌的时候也需要令牌来验签,如果他人获知了我们的密钥
//就可以用我们的密钥来签发我们的JWT令牌,JWT唯一的安全性就是密钥
//别人用我们的密钥来签发我们的JWT令牌就可以随意进入我们的系统
return accessTokenConverter;
} @Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
}
}
3.
package cn.coreqi.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; @Configuration
@EnableAuthorizationServer //开启认证服务器
public class CoreqiAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired
//@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager; @Autowired
private UserDetailsService userDetailsService; @Autowired
private TokenStore jwtTokenStore; @Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter; // @Autowired
// private AuthenticationConfiguration authenticationConfiguration; /**
* 针对端点的配置
* @param authorizationServerEndpointsConfigurer
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
//authorizationServerEndpointsConfigurer.authenticationManager(authenticationConfiguration.getAuthenticationManager());
authorizationServerEndpointsConfigurer.tokenStore(jwtTokenStore) //使用JwtToken
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
if(jwtAccessTokenConverter != null){
authorizationServerEndpointsConfigurer.accessTokenConverter(jwtAccessTokenConverter);
}
} /**
* 第三方应用客户端的有关配置
* @param clientDetailsServiceConfigurer
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
clientDetailsServiceConfigurer.inMemory()
.withClient("coreqi") //client_id
.secret("coreqiSecret") //client_id的密码
.accessTokenValiditySeconds(7200) //令牌的有效时间(单位秒)
.redirectUris("https://www.baidu.com")
.scopes("all","read","write") //所支持的权限有那些
.authorities("COREQI_READ")
.authorizedGrantTypes("authorization_code","password"); //针对当前client所支持的授权模式
} /**
* 针对安全性有关的配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
}
}
⒊如何扩展Jwt Token中的信息
package cn.coreqi.jwt; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer; import java.util.HashMap;
import java.util.Map; public class CoreqiJwtTokenEnhancer implements TokenEnhancer { @Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String,Object> info = new HashMap<>(); //要往accessToken中存放的信息
info.put("company","fanqi");
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info); //设置附加信息
return accessToken;
}
}
package cn.coreqi.config; import cn.coreqi.jwt.CoreqiJwtTokenEnhancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; @Configuration
public class TokenStoreConfig { @Autowired
private RedisConnectionFactory redisConnectionFactory; /**
* TokenStore 负责令牌的存取
* @return
*/
// @Bean
// public TokenStore redisTokenStore(){
// return new RedisTokenStore(redisConnectionFactory);
// } @Configuration
public static class JwtTokenConfig {
/**
* Token生成中的处理
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("fanqi"); //Token签名用的密钥
//发出去的令牌需要密钥签名,验令牌的时候也需要令牌来验签,如果他人获知了我们的密钥
//就可以用我们的密钥来签发我们的JWT令牌,JWT唯一的安全性就是密钥
//别人用我们的密钥来签发我们的JWT令牌就可以随意进入我们的系统
return accessTokenConverter;
} @Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
} @Bean
public TokenEnhancer jwtTokenEnhancer(){
return new CoreqiJwtTokenEnhancer();
}
}
}
package cn.coreqi.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import java.util.ArrayList;
import java.util.List; @Configuration
@EnableAuthorizationServer //开启认证服务器
public class CoreqiAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired
//@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager; @Autowired
private UserDetailsService userDetailsService; @Autowired
private TokenStore jwtTokenStore; @Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired(required = false)
private TokenEnhancer jwtTokenEnhancer; // @Autowired
// private AuthenticationConfiguration authenticationConfiguration; /**
* 针对端点的配置
* @param authorizationServerEndpointsConfigurer
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
//authorizationServerEndpointsConfigurer.authenticationManager(authenticationConfiguration.getAuthenticationManager());
authorizationServerEndpointsConfigurer.tokenStore(jwtTokenStore) //使用JwtToken
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
if(jwtAccessTokenConverter != null && jwtTokenEnhancer != null){
TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); //Token增强器
List<TokenEnhancer> enhancers = new ArrayList<>();
enhancers.add(jwtTokenEnhancer);
enhancers.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(enhancers);
authorizationServerEndpointsConfigurer.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
} /**
* 第三方应用客户端的有关配置
* @param clientDetailsServiceConfigurer
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
clientDetailsServiceConfigurer.inMemory()
.withClient("coreqi") //client_id
.secret("coreqiSecret") //client_id的密码
.accessTokenValiditySeconds(7200) //令牌的有效时间(单位秒)
.redirectUris("https://www.baidu.com")
.scopes("all","read","write") //所支持的权限有那些
.authorities("COREQI_READ")
.authorizedGrantTypes("authorization_code","password"); //针对当前client所支持的授权模式
} /**
* 针对安全性有关的配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
}
}
⒋控制器如何识别Token中扩展的信息
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
package cn.coreqi.controller; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.Authenticator; @RestController
public class UserController { @GetMapping("/user/me")
public Object getCurrentUser(Authenticator user , HttpServletRequest request) throws UnsupportedEncodingException {
String header = request.getHeader("Authorization");
String token = StringUtils.substringAfter(header,"bearer ");
Claims claims = Jwts.parser().setSigningKey("fanqi".getBytes("UTF-8")).parseClaimsJws(token).getBody();
String company = (String) claims.get("company");
System.out.println(company);
return user;
}
}