Authentication and authorization - based on OAuth2
1, Establishment of OAuth2 authentication service - based on memory
1. Create project import dependency
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
The above dependencies are based on Cloud-Hoxton.SR12, Boot-2.3.12.RELEASE. Dependencies are very important. Please refer to the official documents when building.
2. Configure authentication server
/** * OAuth2 Authentication policies client access related policies */ @Configuration public class OAuth2Config extends AuthorizationServerConfigurerAdapter { /** * First, get the authentication manager from the container. What is it to be queried */ @Autowired private AuthenticationManager authenticationManager; /** * Intuitively, the service for obtaining user details from the container is the class for obtaining user information */ @Autowired private UserDetailsService userDetailsService; /** * Rewrite the configure method. Note that the authentication and authorization policies for client access are configured here * So the parameter is ClientDetailsServiceConfigurer * * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception{ // Reading information from memory is of course more rigorous. You should get the configuration information from the database or configuration file clients.inMemory() // The id of the client is equivalent to the registration id of the client .withClient("eate") // password .secret("zzc") // Authorization type token password client certificate .authorizedGrantTypes( "refresh_token", "password", "client_credentials") // Authority function or scope of authorization .scopes("webclient","mobileclient"); } /** * Define the container components to be used for authentication authorization, that is, the components automatically injected from above * * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception{ // Set authentication authorization component endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
Create the configuration class OAuth2Config to inherit the AuthorizationServerConfigurerAdapter class.
3. Configure user authentication method and information
/** * Configure the policy of OAuth2 to configure the policy of accessing the authentication server */ @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * The old rule is that the encryptor will report an error if it is not configured * * @return */ @Bean public PasswordEncoder encoder(){ return NoOpPasswordEncoder.getInstance(); } /** * The default authentication manager of Security is selected here * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception{ return super.authenticationManager(); } /** * Select the default user details service * * @return * @throws Exception */ @Bean @Override public UserDetailsService userDetailsServiceBean() throws Exception{ return super.userDetailsServiceBean(); } /** * Security The three configuration methods overload the configuration method to configure user related information * * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.inMemoryAuthentication() .withUser("john") .password("password") .roles("admin") .and() .withUser("mix") .password("123") .roles("user") .and() .passwordEncoder(encoder()); } }
4. Add annotations and callback endpoints
@RestController @SpringBootApplication @EnableResourceServer @EnableAuthorizationServer public class AuthticationServerApplication { public static void main(String[] args) { SpringApplication.run(AuthticationServerApplication.class, args); } @GetMapping(value = "/user", produces = {"application/json"}) public Map<String,Object> user(OAuth2Authentication user){ Map<String, Object> userInfo = new HashMap<>(); userInfo.put("user",user.getUserAuthentication().getPrincipal()); userInfo.put("authroizaties", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities())); return userInfo; } }
5. Access test
To access this path: http://localhost:8080/oauth/token
Get the following Token:
{ "access_token": "5e03b5c5-8c2a-4d4a-95fe-be358c4303a0", "token_type": "bearer", "refresh_token": "c48d4964-ee22-46d2-b010-9fae65ea7d45", "expires_in": 43030, "scope": "webclient" }
When accessing, pay attention to adding client authentication and user authentication information.
6. Summary on the establishment of authentication and authorization services
i. Customize two sets of authentication schemes for the client and users using the client:
The client authentication policy configuration class needs to implement the AuthorizationServerConfigurerAdapter, which is in oauth2 dependency.
The class structure is as follows:
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer { public AuthorizationServerConfigurerAdapter() { } public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { } public void configure(ClientDetailsServiceConfigurer clients) throws Exception { } public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { } }
At present, only the second and third configuration methods are used to configure the client access policy.
The authentication policy configuration class of the user using the client needs to implement WebSecurityConfigurerAdapter, which is the same as ordinary security in the security dependency.
ii. Add relevant notes:
@EnableAuthorizationServer
This annotation declares that this class is an authentication authorization server.
@EnableResourceServer
This annotation declares that this class is also a resource provider. It enforces a filter to check whether the incoming request has an OAuth2 access token. Then use the callback to check whether the token is valid (explained in detail when configuring the resource server).
2, Establishment of OAuth2 resource service - based on memory
1. Create project import dependency
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
2. Configure access policy
/** * Implement resource server access policy configuration */ @Configuration public class ResourceServerConfig extends ResourceServerConfigurerAdapter { /** * The access policy is simple, authenticating all requests * * @param http * @throws Exception */ @Override public void configure(HttpSecurity http) throws Exception{ http.authorizeRequests().anyRequest().authenticated(); } }
3. Add annotation
@SpringBootApplication @EnableResourceServer public class ResourceServiceApplication { public static void main(String[] args) { SpringApplication.run(ResourceServiceApplication.class, args); } }
4. Write configuration
server: port: 8881 security: oauth2: resource: user-info-uri: http://localhost:8080/user
Configure the callback url, which points to the specified endpoint of the authentication and authorization service.
5. Summary of resource services
i. Configure resource service access policy:
The resource service access policy configuration class needs to implement the ResourceServerConfigurerAdapter class, which is in oauth2 dependency.
The class structure is as follows:
public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer { public ResourceServerConfigurerAdapter() { } public void configure(ResourceServerSecurityConfigurer resources) throws Exception { } public void configure(HttpSecurity http) throws Exception { ((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated(); } }
At present, only the second configuration class is used to restrict the interception of http requests.
3, Using spring cloud oauth2 + JWT to implement authentication - authentication server
OAuth2 is a token based authentication framework, but ironically, it does not provide any standard for how to define tokens in its specification. In order to correct the defects of OAuth2 token standard, a new standard named JSON Web Token stands out.
JWT features:
1. Compact – JWT tokens are Base64 encoded and can be easily passed through URL, HTTP header or POST parameters.
2. Password signature – the JWT token is issued in front of his authentication server to ensure that it has not been tampered with.
3. Self contained – because there is a signature, there is no need to confirm the token content through the authentication server, and the content can be checked by the service.
4. Extensible – when the authentication server generates a token, additional information can be placed before it is sealed, and the receiving server can decrypt and obtain the payload.
1. Add dependency
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.0.4.RELEASE</version> </dependency>
2. Add configuration (the steps are quite complicated =.)
I. JWTTokenStoreConfig configuration class:
/** * Configure security and use jwt as authentication means */ @Configuration public class JWTTokenStoreConfig { /** * A common configuration class is used to obtain the key from the configuration file */ @Autowired private ServiceConfig serviceConfig; /** * Define the token storage type as JwtTokenStore * This class is responsible for the encapsulation of tokens in CloudSecurity * It is used to save the token (encapsulated in OAuth2AccessToken) * Details Baidu * * @return */ @Bean public TokenStore tokenStore(){ return new JwtTokenStore(jwtAccessTokenConverter()); } /** * Set default TokenServices * Inject the above JwtTokenStore * This class is responsible for generating, obtaining and refreshing tokens according to the type of TokenStore * * @return */ @Bean @Primary public DefaultTokenServices tokenServices(){ DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); // Set the type of Token to be generated defaultTokenServices.setTokenStore(tokenStore()); // Set allow refresh Token defaultTokenServices.setSupportRefreshToken(true); return defaultTokenServices; } /** * Create JWT token converter * * @return */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(serviceConfig.getJwtSigningKey()); return converter; } /** * This class is responsible for adding additional data to the JWT * * @return */ @Bean public TokenEnhancer jwtTokenEnhancer(){ return new JWTTokenEnhancer(); } }
This class is a configuration class responsible for the creation, signature and translation of JWT tokens.
Important classes:
// JWT token converter JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(serviceConfig.getJwtSigningKey()); // Class used to set token type TokenStore = new JwtTokenStore(jwtAccessTokenConverter()); // The default class used to generate token in oauth2 DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(tokenStore());
After the above steps, you can convert the OAuth2 default token into a JWT token.
II. Create a class to read the configuration:
@Component @Configuration public class ServiceConfig { @Value("${{signing.key}") private String jwtSigningKey = ""; public String getJwtSigningKey() { return jwtSigningKey; } }
III. create token enhancer implementation class:
public class JWTTokenEnhancer implements TokenEnhancer { private String getOrgId(String userName){ return "ZZC"; } @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> additionalInfo = new HashMap<>(); String orgId = getOrgId(authentication.getName()); additionalInfo.put("organizationId", orgId); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; } }
The source code of TokenEnhancer interface is as follows:
package org.springframework.security.oauth2.provider.token; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; public interface TokenEnhancer { OAuth2AccessToken enhance(OAuth2AccessToken var1, OAuth2Authentication var2); }
You can rewrite the enhance method by implementing the interface to add information to the Token. The Authentication object is stored in the second parameter oauthauthentication of the method, which can be used to obtain user information. The first parameter is the Token of OAuth2.
DefaultOAuth2AccessToken implements the interface OAuth2AccessToken and provides a method to add additional information to the token.
Ⅳ. OAuth2Config configuration class:
/** * OAuth2 Authentication policies client access related policies */ @Configuration public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired private TokenEnhancer jwtTokenEnhancer; @Autowired private TokenStore tokenStore; @Autowired private DefaultTokenServices defaultTokenServices; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; /** * First, get the authentication manager from the container. What is it to be queried */ @Autowired private AuthenticationManager authenticationManager; /** * Intuitively, the service for obtaining user details from the container is the class for obtaining user information */ @Autowired private UserDetailsService userDetailsService; /** * Rewrite the configure method. Note that the authentication and authorization policies for client access are configured here * So the parameter is ClientDetailsServiceConfigurer * * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception{ // Reading information from memory is of course more rigorous. You should get the configuration information from the database or configuration file clients.inMemory() // The id of the client is equivalent to the registration id of the client .withClient("eate") // password .secret("zzc") // Authorization type token password client certificate .authorizedGrantTypes( "refresh_token", "password", "client_credentials") // Authority function or scope of authorization .scopes("webclient","mobileclient"); } /** * Define the container components to be used for authentication authorization, that is, the components automatically injected from above * * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception{ // Create an enhancer chain and add two enhancers to the chain TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer, jwtAccessTokenConverter)); // Set authentication authorization component endpoints .tokenStore(tokenStore) .tokenEnhancer(tokenEnhancerChain) .accessTokenConverter(jwtAccessTokenConverter) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
Most of the configuration contents are the same as before. Let's take a look at the differences:
① , * * an enhancer chain is declared in the configure(AuthorizationServerEndpointsConfigurer endpoints) * * method
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer,jwtAccessTokenConverter));
Why declare it in this configuration method? Because this configuration method is responsible for setting the authentication endpoint, the AuthorizationServerEndpointsConfigurer parameter is provided to customize the authentication scheme.
② JwtAccessTokenConverter can be directly added to the enhancer chain
public class JwtAccessTokenConverter implements TokenEnhancer, AccessTokenConverter, InitializingBean
This is a class declaration. In essence, JwtAccessTokenConverter is also a TokenEnhancer.
③ Inject all the previously declared configurations and tell the authorization server endpoints configurator to issue the token of the custom rule.
3. Write yaml configuration key
signing: key: zzc123456
Other parts do not need to be changed.
4, Using spring cloud oauth2 + JWT to implement authentication - resource side
Well, it doesn't seem to need any changes for the time being.