Practical tutorial of using JWT token in OAuth2.0 series (8)
OAuth2.0 blog series:
- Basic concepts and operation process of OAuth2.0 series (1)
- OAuth 2.0 series authorization code mode practice tutorial (2)
- OAuth2.0 series of simplified mode Practice Course (3)
- OAuth2.0 series password mode practice tutorial (4)
- OAuth2.0 series of client mode practice tutorial (5)
- OAuth2.0 series information database storage tutorial (6)
- OAuth2.0 series information Redis storage tutorial (7)
- JWT token practice tutorial of OAuth2.0 series (8)
- Integrated JWT of OAuth2.0 series for single sign on
1. Introduction to the preface
stay Previous articles We learned some basic concepts of OAuth2, had a basic understanding of OAuth2, and also stored the tokens of OAuth2.0 in the database, corresponding to the blog: jdbc data storage And then what if you don't want to store tokens?
In IDEA, Ctrl+Alt+B, you can see the implementation of TokenStore as follows:
ok, in fact, there are the above ways for token storage, which are introduced respectively:
- InMemoryTokenStore, default store, saved in memory
- JdbcTokenStore,access_token stored in database
- JwtTokenStore, JWT is a special way. It is a stateless way of storage. It does not store memory or database. It only carries comprehensive user information in JWT, which can be saved in JWT with past verification
- RedisTokenStore, access_ The token is saved in redis.
- JwkTokenStore, access_ The token is saved to the JSON Web Key.
2. Example experiment verification
Preparation of experimental environment:
- IntelliJ IDEA
- Maven 3. + version
Create a new SpringBoot Initializer project, which can be named oauth2 JWT
The following configurations are mainly added:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Cloud Oauth2--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <!-- Spring Cloud Security--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency>
TokenStore:
@Bean public TokenStore jwtTokenStore() { //Implementation of Access Token based on jwt return new JwtTokenStore(accessTokenConverter()); }
JwtAccessTokenConverter :
@Bean public JwtAccessTokenConverter accessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){ @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { String grantType = authentication.getOAuth2Request().getGrantType(); //Only authorization code and password mode can customize token information if(AUTHORIZATION_CODE.equals(grantType) || GRANT_TYPE_PASSWORD.equals(grantType)) { String userName = authentication.getUserAuthentication().getName(); // Customize some token information Map<String, Object> additionalInformation = new HashMap<String, Object>(16); additionalInformation.put("user_name", userName); additionalInformation = Collections.unmodifiableMap(additionalInformation); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation); } OAuth2AccessToken token = super.enhance(accessToken, authentication); return token; } }; // Set signing key converter.setSigningKey("signingKey"); return converter; }
Configure accessTokenConverter
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).authenticationManager(authenticationManager) //Custom accessTokenConverter .accessTokenConverter(accessTokenConverter()) //Support to obtain token Mode .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS); }
General configuration class reference:
package com.example.springboot.oauth2.configuration; 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.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; 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.OAuth2Authentication; 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 java.util.Collections; import java.util.HashMap; import java.util.Map; /** * <pre> * OAuth2.0 Configuration class * </pre> * * <pre> * @author mazq * Modification record * Revised version: modified by: modified on: 11:44, June 17, 2020 * </pre> */ @Configuration @EnableAuthorizationServer public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter { private static final String CLIENT_ID = "cms"; private static final String SECRET_CHAR_SEQUENCE = "{noop}secret"; private static final String SCOPE_READ = "read"; private static final String SCOPE_WRITE = "write"; private static final String TRUST = "trust"; private static final String USER ="user"; private static final String ALL = "all"; private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 2*60; private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 2*60; // Password mode authorization mode private static final String GRANT_TYPE_PASSWORD = "password"; //Authorization code mode private static final String AUTHORIZATION_CODE = "authorization_code"; //refresh token Mode private static final String REFRESH_TOKEN = "refresh_token"; //Simplified licensing model private static final String IMPLICIT = "implicit"; //Specify which resources require authorization authentication private static final String RESOURCE_ID = "resource_id"; @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients // Using memory storage .inMemory() //Tag client id .withClient(CLIENT_ID) //Client security code .secret(SECRET_CHAR_SEQUENCE) //Direct automatic authorization for true returns code successfully .autoApprove(true) .redirectUris("http://127.0.0.1:8084/cms/login") //Redirect uri //Authorized scope .scopes(ALL) //token time seconds .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS) //Refresh token time seconds .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS) //Allowed authorization type .authorizedGrantTypes(GRANT_TYPE_PASSWORD , AUTHORIZATION_CODE , REFRESH_TOKEN , IMPLICIT); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).authenticationManager(authenticationManager) //Custom accessTokenConverter .accessTokenConverter(accessTokenConverter()) //Support to obtain token Mode .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS); } /** * Security configuration of authentication server * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security // Open / oauth/token_key authentication port authentication permission access .tokenKeyAccess("isAuthenticated()") // Open / oauth/check_token authentication port authentication permission access .checkTokenAccess("isAuthenticated()") //Allow form authentication .allowFormAuthenticationForClients(); } @Bean public JwtAccessTokenConverter accessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){ @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { String grantType = authentication.getOAuth2Request().getGrantType(); //Only authorization code and password mode can customize token information if(AUTHORIZATION_CODE.equals(grantType) || GRANT_TYPE_PASSWORD.equals(grantType)) { String userName = authentication.getUserAuthentication().getName(); // Customize some token information Map<String, Object> additionalInformation = new HashMap<String, Object>(16); additionalInformation.put("user_name", userName); additionalInformation = Collections.unmodifiableMap(additionalInformation); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation); } OAuth2AccessToken token = super.enhance(accessToken, authentication); return token; } }; // Set signing key converter.setSigningKey("signingKey"); return converter; } @Bean public TokenStore jwtTokenStore() { //Implementation of Access Token based on jwt return new JwtTokenStore(accessTokenConverter()); } }
Spring security configuration class:
package com.example.springboot.oauth2.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * <pre> * Spring Security Configuration class * </pre> * * <pre> * @author mazq * Modification record * Revised version: modified by: modified on: 10:39, June 15, 2020 * </pre> */ @Configuration @EnableWebSecurity @Order(1) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.inMemoryAuthentication() auth.inMemoryAuthentication() .withUser("nicky") .password("{noop}123") .roles("admin"); } @Override public void configure(WebSecurity web) throws Exception { //Solve the problem of static resources being intercepted web.ignoring().antMatchers("/asserts/**"); web.ignoring().antMatchers("/favicon.ico"); } @Override protected void configure(HttpSecurity http) throws Exception { http // Configure landing page and allow access .formLogin().permitAll() // to configure Basic Sign in //.and().httpBasic() // Configure logout page .and().logout().logoutUrl("/logout").logoutSuccessUrl("/") .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll() // All other requests need authentication .anyRequest().authenticated() // Turn off cross domain protection; .and().csrf().disable(); } }
3. Simple function test
Access the authorization link, and access it in the browser. The authorization code mode response_type parameter to code:
http://localhost:8888/oauth/authorize?client_id=cms&client_secret=secret&response_type=code
Because there is no login, the default login page of spring security will be returned. The specific code is http. Formlogin(). Permitall(); if you want to pop-up login, you can configure http.httpBasic();, there is no login page for this configuration. Custom login page can be configured in this way http.formLogin().loginPage("/login").permitAll()
As shown in the figure, enter the database password configured by spring security
Login succeeded, return to redirect_uri, get the authorization code
Redirect back to redirect_uri, http://localhost:8084/cms/login?code=???
Configure the authorization parameters of the request header. In Basic Auth mode, username is the client_id, password is the client_secret
After getting the authorization code, get the token. This tutorial uses the authorization code method
JWT token
{ "access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTIzNzYwNjEsInVzZXJfbmFtZSI6Im5pY2t5IiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9hZG1pbiJdLCJqdGkiOiJiM2IwZGExNS1mMmQyLTRlN2MtYTUwNC1iMzg5YjkxMjM0MDMiLCJjbGllbnRfaWQiOiJjbXMiLCJzY29wZSI6WyJhbGwiXX0.TpIBd9Gtb4M7sC1MSQsxsn8mwnhAm59CUBZPU7jwdnE", "token_type":"bearer", "refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJuaWNreSIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJiM2IwZGExNS1mMmQyLTRlN2MtYTUwNC1iMzg5YjkxMjM0MDMiLCJleHAiOjE1OTIzNzYwNjEsImF1dGhvcml0aWVzIjpbIlJPTEVfYWRtaW4iXSwianRpIjoiODVhYTlmMGYtNDliNS00NDg4LTk4MTQtNmM0MmZjMjZkYTc2IiwiY2xpZW50X2lkIjoiY21zIn0.TU8ZD_5AxRGbgbOWZSuWAxwWjMJ4HLHniA46M-dnChE", "expires_in":119, "scope":"all", "user_name":"nicky", "jti":"b3b0da15-f2d2-4e7c-a504-b389b9123403" }
Example code download: code download