-
Notifications
You must be signed in to change notification settings - Fork 60
Use JWT as Access Token
JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for creating access tokens. There are some facts about JWT:
- Encoded into Base64.
- The authorized entity can decrypt a JWT to JSON.
- JWT allows you to add custom fields into JSON.
For changing your authentication eco-system to use JWT token, you need to do several things:
Besides the Spring Security library and the OAuth2 library, the Spring Security JWT library needs to be added.
dependencies {
// Spring Security and OAuth2
compile group: 'org.springframework.cloud', name: 'spring-cloud-security'
compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '2.1.3.RELEASE'
// Spring Security JWT
compile group: 'org.springframework.security', name: 'spring-security-jwt', version: '1.0.0.RELEASE'
// For storing credentials and roles in database
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.5.0.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-autoconfigure', version: '1.5.0.RELEASE'
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.38'
}
Like encrypting passwords in the config server, you need set up a symmetric encryption key by adding signing.key
into the configuration of the authentication service.
You can either add it to the local application.yml
file or the remote authenticationservice.yml
file if you moved the configuration into the config server.
application.yml
signing:
key: 345345fsdfsf5345
signing.key: "345345fsdfsf5345"
@Configuration
public class JWTTokenStoreConfig {
@Autowired
private ServiceConfig serviceConfig;
/**
* Create a new JWT token store.
*
* @return The {@code TokenStore} object.
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* Generate token services.
*
* <p>This method will use the Spring security’s default token services
* implementation which tokens will be generated as random UUID values.
*
* @return The {@code DefaultTokenServices} object.
*/
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
/**
* Generate the converter for translating the token.
*
* @return The {@code JwtAccessTokenConverter} object with the signing
* key.
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(serviceConfig.getJwtSigningKey()); // Set the signing key that will be used to sign your token (define in configuration)
return converter;
}
/**
* Create a new JWT token enhancer.
*
* <p>The {@code JWTTokenEnhancer} class will help on adding custom fields
* into a JWT token. Currently, the organization Id field is added into
* the JWT token.
*
* @return The {@code TokenEnhancer} object.
*/
@Bean
public TokenEnhancer jwtTokenEnhancer() {
return new JWTTokenEnhancer();
}
}
@Configuration
public class JWTOAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private DefaultTokenServices tokenServices;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private TokenEnhancer jwtTokenEnhancer;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer, jwtAccessTokenConverter)); // Spring Security allows you to hook multiple token enhancers
endpoints.tokenStore(tokenStore) // The JWTTokenStoreConfig.tokenStore() will be injected in here
.accessTokenConverter(jwtAccessTokenConverter) // The JWTTokenStoreConfig.jwtAccessTokenConverter() will be injected in here
.tokenEnhancer(tokenEnhancerChain) // The chain of token enhancers will be passed into the endpoint
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // Define what client applications are registered with the service
clients.inMemory() // Store the application information in memory
.withClient("eagleeye") // Specify which client application will register
.secret("thisissecret") // Specify the secret which will be used to get the access token
.authorizedGrantTypes("refresh_token", "password", "client_credentials") // Provide a list of the authorization grant types that will be supported by the service
.scopes("webclient", "mobileclient"); // Define the types of the client applications can get the access token from the service
}
}
We will make a same request to the endpoint of the authentication service to get a JWT token.
-
Method: POST
-
Authentication
- Type: Basic Auth
- Username: eagleeye
- Password: thisissecret
-
Body:
Key Value grant_type password scope webclient username john.carnell password password1
After sending the HTTP request to the authentication service with the credential, it will return the JWT token:
{
"access_token": "eyJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb25JZCI6ImQxODU5ZjFmLTRiZDctNDU5My04NjU0LWVhNmQ5YTZhNjI2ZSIsInVzZXJfbmFtZSI6ImpvaG4uY2FybmVsbCIsInNjb3BlIjpbIndlYmNsaWVudCJdLCJleHAiOjE1NTQ4OTQ1NTQsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI1N2I3ZGVjMi1iZGQ0LTQxYzUtOWVlZC1mM2Q1MjVmMzVmODEiLCJjbGllbnRfaWQiOiJlYWdsZWV5ZSJ9.OPcJzKnDg8g1rOhU10SIgbWPOcZIEswqcsFlbIPxfiw",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb25JZCI6ImQxODU5ZjFmLTRiZDctNDU5My04NjU0LWVhNmQ5YTZhNjI2ZSIsInVzZXJfbmFtZSI6ImpvaG4uY2FybmVsbCIsInNjb3BlIjpbIndlYmNsaWVudCJdLCJhdGkiOiI1N2I3ZGVjMi1iZGQ0LTQxYzUtOWVlZC1mM2Q1MjVmMzVmODEiLCJleHAiOjE1NTc0NDMzNTQsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI5NGQxNzIxNi03ZGFhLTQ4ZmMtYmJkYy0yNmU2YTZjNTBjNmYiLCJjbGllbnRfaWQiOiJlYWdsZWV5ZSJ9.UUXCaDh8kT6wip8Uuf7GRDn8szqjx5FVu8BkOfuREd4",
"expires_in": 43199,
"scope": "webclient",
"organizationId": "d1859f1f-4bd7-4593-8654-ea6d9a6a626e",
"jti": "57b7dec2-bdd4-41c5-9eed-f3d525f35f81"
}
Besides the Spring Security library and the OAuth2 library, the Spring Security JWT library needs to be added.
build.gradle
dependencies {
// Spring Security and OAuth2
compile group: 'org.springframework.cloud', name: 'spring-cloud-security'
compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '2.1.3.RELEASE'
// Spring Security JWT
compile group: 'org.springframework.security', name: 'spring-security-jwt', version: '1.0.0.RELEASE'
}
Similar to the JWTTokenStoreConfig.java
in the authentication service, the protected service needs the JWTTokenStoreConfig.java
to define the logic of creating, signing and translating JWT token.
JWTTokenStoreConfig.java
@Configuration
public class JWTTokenStoreConfig {
@Autowired
private ServiceConfig serviceConfig;
//JWT
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(serviceConfig.getJwtSigningKey());
return converter;
}
}
For the protected service, you still need to define the permissions about who or which role can access the protected service.
- Give permissions to all authenticated users:
ResourceServerConfiguration.java
@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
- Give permissions to a group of users with certain role:
ResourceServerConfiguration.java
@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers(HttpMethod.DELETE, "/v1/organizations/**")
.hasRole("ADMIN") // Only ADMIN role have the permission to do DELETE operations
.anyRequest()
.authenticated();
}
}
- Overview
- Getting Started
-
Technical Essentials
- Autowired
- SpringData JPA
- Configuration File Auto-loading
- Configuration Encryption
- Service Discovery with Eureka
- Resiliency Patterns with Hystrix
- Configure Hystrix
- Service Gateway with Zuul
- Zuul Filters
- Protect Service with Spring Security and OAuth2
- Use JWT as Access Token
- Store Clients and Users' Credentials to DB
- Integrate with Message Queue (Kafka)
- Integrate with Redis
- Tune Logging
- Log Aggregation
- Send Trace to Zipkin
- Build Runnable Jar
- Core Application Logic
- Components