First, unified authentication ideas under the micro-service architecture
-
Session-based authentication: In the distributed environment, session-based authentication has a problem. Each application service needs to store the user identity information in the Session. To allocate local requests to another application service through load balancing, the Session information needs to be carried over. We can use Session sharing, Session sticky, etc.
The Session scheme also has disadvantages, such as cookie-based and ineffective use on mobile devices
-
Token-based authentication: In token-based authentication, the server does not need to store authentication data, which is easy to maintain and extensible. The client can store the token in any place. In addition, the web and APP can implement unified authentication mechanism. The disadvantages of token are also obvious. Because token contains self-contained information, the amount of data is generally large. In addition, each request needs to be transmitted, so it consumes more bandwidth. In addition, the token signature verification operation will also bring additional processing burden to the CPU.
OAuth2 open licensing agreement/standard
1. The OAuth2 is introduced
OAuth (Open Authorization) is an open protocol/standard that allows users to authorize third party applications to access their information stored on another service provider without having to provide a user name and password to third party applications or share all the content of their data.
OAuth2 is a continuation version of OAuth protocol, but it is not backward compatible with OAuth1, that is, OAuth1 is completely abolished.
Allows users to authorize third-party applications to access their information stored on another service provider without having to provide the user name and password to the third-party application or share all the content of their data
Example: All third party login authorization is OAuth, do not want to register in the third party website, you can use QQ, wechat authorization login, open part of the function.
2. OAuth2 protocol roles and processes
- Resource Owner: refers to the user himself
- Client: A website or application that we want to log in to, such as Dragnet
- Authorization Server: can be understood as wechat or QQ
- Resource Server: can be understood as wechat or QQ
3. When should OAuth2 be used?
- Third party authorized login scenarios: wechat authorized login, QQ authorized login, Weibo authorized login, etc., which are typical OAuth2 usage scenarios.
- Single sign-on (SSO) scenario: in the micro-service scenario, an authentication center can be created (acting as an authentication platform). All services need to be authenticated by this authentication center. After only one login, services within multiple authorization scopes can be serialized freely.
4. Token authorization mode of OAuth2
- Authorization-code
- Password provides a user name + password for a token
- Implicit
- Client credentials
The authorization code mode uses the callback address, which is the most complex authorization mode. This mode is used for third-party login such as Weibo, wechat and QQ. Generally, the password mode is used (provide user name + password in exchange for token).
Spring Cloud OAuth2 + JWT implementation
1. Introduction to Spring Cloud OAuth2
Spring Cloud OAuth2 is the implementation of OAuth2 protocol in Spring Cloud system, which can be used to do unified authentication (verify identity legitimacy) authorization (verify permission) of multiple micro-services. Centralized authentication and authorization is achieved by sending some type of Grant_type to the OAuth2 service (Unified Authentication and Authorization Service) to obtain an Access_token that is trusted by other microservices.
Note: The essence of using OAuth2 to solve the problem is to introduce an authentication and authorization layer that connects to the owner of the resource. In the authorization layer, the owner of the resource can grant third-party applications access to some of our protected resources.
2. Spring Cloud OAuth2 to build a unified authentication service idea for micro services
Note: In our unified authentication scenario, Resource Server is actually our various protected micro-services, various API access interfaces in micro-services are resources, and the browser that initiates HTTP requests is the Client Client (corresponding to third-party applications). Resource server is to land from tripartite (for instance QQ) the information such as the head picture name that gets over there.
3. (Enter development) Set up the Authorization Server and Resource Server.
Set up the Authorization Server and issue tokens
-
Create a new project lagou-Cloud-Oauth-server-9999
-
Introduce the dependency pom.xml
<! Import Eureka Client dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<! Spring Cloud oAuth2 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> <exclusions> <exclusion> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.11. RELEASE</version> </dependency> <! Security support for oauth2 --> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.3.4. RELEASE</version> </dependency> Copy the code
- Application.yml (build authentication server, no special configuration file)
server:
port: 9999
Spring:
application:
name: lagou-cloud-oauth-server
datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/oauth2? useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true username: root password: 123456 druid: initialSize: 10 minIdle: 10 maxActive: 30 maxWait: 50000 eureka: client: serviceUrl: Path to eureka Server defaultZone: http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/ Eureka server can synchronize the registry with each eureka server instance: Use the IP address to register, otherwise the host name will be registered. prefer-ip-address: true # customize the display format of the instance, plus the version number, convenient multi-version management, note that the address is ip-address, the earlier version is ipAddress instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@ Copy the code
- Nothing special about the entry class (@springBootApplication @EnableDiscoveryClient is still the second bit)
- Authentication server configuration class
package config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager; 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.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; / * * * @author: 190coder <190coder.cn> * @description: The current class is the configuration class of Oauth2 Server (needs to inherit the specific parent class) * @create: the 2020-07-30 20:03* / @Configuration @EnableAuthorizationServer // Enable the authentication server public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; / * ** Authentication server ultimately provides services in the form of API interface (verify validity and generate tokens, verify tokens, etc.)* Then, using the API interface to external, it involves the interface access permission, we need to do the necessary configuration here * * @param security * @throws Exception * / @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { super.configure(security); // Turn on endPoints so that we can access the interface later security. // Allow client form authentication allowFormAuthenticationForClients() // enable port /oauth/token_key access permission (allow) .tokenKeyAccess("permitAll()") // enable port /oauth/check_token access permission (allowed) .checkTokenAccess("permitAll()"); } / * ** Customer review configuration,* For example client_id, secret* At present, this service is just like QQ platform. As a client, Dragnet needs QQ platform for login authorization and authentication, which is required in advanceTo register QQ platform, QQ platform will give the hook net* Issue necessary parameters such as client_id to indicate who the client is * * @param clients * @throws Exception * * / @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { super.configure(clients); clients. Where is the client information stored, either in memory or in a database inMemory() // Add a client configuration specifying its client_id .withClient("client_lagou") // Specify the client password/security code .secret("abcxyz") // Specify the resources that can be accessed by the client .resourceIds("autodeliver") // The authentication type/token issuing mode can be configured here, but not all of them can be used. The specific way to issue tokens needs to be specified when the client calls .authorizedGrantTypes("password"."refresh_token") // Client permission range. Set this parameter to all .scopes("all"); } / * ** The authentication server is playing with tokens, so configure token management here (token is a characterString, the current token needs to be stored on the server side, so where is it stored? It's all configured here) * * @param endpoints * @throws Exception * / @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { super.configure(endpoints); endpoints // Specify the token storage method .tokenStore(tokenStore()) // A description of the token service, which can be thought of as a description of the token generation details, such as the validity period, etc .tokenServices(authorizationServerTokenServices()) // Specify the authentication manager and inject one into the current class .authenticationManager(authenticationManager) .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); } / * ** This method allows the user to obtain a token service object (which describes the token validity period and other information). * * @return * / private AuthorizationServerTokenServices authorizationServerTokenServices(a) { // Use the default implementation DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); // Whether to enable token refresh defaultTokenServices.setSupportRefreshToken(true); // In what form are tokens stored defaultTokenServices.setTokenStore(tokenStore()); // Access_token is the token we need to carry when requesting resources defaultTokenServices.setAccessTokenValiditySeconds(30); // Set the refresh token validity period to 3 days defaultTokenServices.setRefreshTokenValiditySeconds(259200); return defaultTokenServices; } / * ** This method is used to create tokenStore objects (token store objects) in what form are tokens stored* / public TokenStore tokenStore(a){ return new InMemoryTokenStore(); } } Copy the code
Configuration class interpretation:
-
About the three configure methods
- Configure (ClientDetailsServiceConfigurer clients) : the customer details information here is initialized, you can write the customer details mood information die here or database to store, by obtaining information for details
- Configure (AuthorizationServerEndpointsConfigurer endpoints) : configuration access token (token) endpoint and token service (token services) and a storage medium
- Configure (AuthorizationServerSecurityConfigurer oauthServer) : configuration access permissions and token service (token services)
-
About TokenStore
- InMemoryTokenStore: Stores memory. By default, it can perfect work on single server (i.e. access concurrency value under the condition of pressure is not big, and it at the time of failure will not make a backup), most of the projects can use this version of the implementation to try, you can use it to manage the development, because will not be saved to disk, so easier to debug.
- JdbcTokenStore: This is a JDBC-based implementation version where tokens are stored in a relational database. With this version of the implementation, you can share token information between different servers, and be sure to add the “SpringJDBC” dependency to your classpath.
- JwtTokenStore: JSON Web Token (JWT), it encodes token-related data (so it doesn’t need to be stored for back-end services, which is a big advantage). The downside is that this Token takes up a lot of space. If you add more user credentials, JwtTokenStore does not store any data.
- Authentication server security configuration class
package config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.ArrayList; / * * * @author: 190coder <190coder.cn> * @description: This configuration class verifies user names and passwords * @create: the 2020-07-30 20:52* / @Configuration public class SecurityConfiger extends WebSecurityConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; / * ** Register an authentication manager object to the container * * @throws Exception * / @Bean @Override public AuthenticationManager authenticationManagerBean(a) throws Exception { return super.authenticationManagerBean(); } / * * * * Handle username and password authentication* 1) The client passes username and password parameters to the authentication server* 2) In general, username and password are stored in the user table in the database* 3) Verify the validity of the user information currently transmitted according to the data in the user table * * / @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // In this method, we can disassociate the database. For now, we can configure the user information in memory // Instantiate a user object (equivalent to a user record in a data table) UserDetails user = new User("admin"."123456".new ArrayList<>()); auth.inMemoryAuthentication() .withUser(user).passwordEncoder(passwordEncoder); } / * ** Password encoding object (passwords are not encrypted) * @return * / @Bean public PasswordEncoder passwordEncoder(a) { return NoOpPasswordEncoder.getInstance(); } } Copy the code
- Test: Start the Eureka Server registry and authentication service
Visit: http://localhost:9998/oauth/token? client_secret=abcxyz&grant_type=password&username=admin&password=123456&client_id=client_lagou
-
The endpoint: / request/token
-
Gets the parameters carried by the token
- Client_id: indicates the ID of a client
- Client_secret: indicates the single password of the client
- Grant_type: Specifies which issuance type to use, password
- Username: indicates the username
- Password: password
-
Check token: http://localhost:9998/oauth/check_token? token=1068b57c-0b2d-4789-8ce5-fa968c402d0a
-
The refresh token: http://localhost:9998/oauth/token? grant_type=refresh_token&client_id=client_lagou&client_secret=abcxyz&refresh_token=7c47f959-17a6-4a09-8e7b-0f49bfaef725
Set up a Resource Server (want access to authenticated microservices) Resource Server configuration
-
Import the OUth2 jar package
-
Resource service configuration class
package com.lagou.edu.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.RemoteTokenServices; / * * * @author: 190coder <190coder.cn> * @description: Resource service configuration class * @create: the 2020-07-30 mark* / @Configuration @EnableResourceServer // Enable the resource server @EnableWebSecurity // Enable Web access security public class ResourceServerConfiger extends ResourceServerConfigurerAdapter { private String sign_key = "190coder.cn"; // JWT signature key / * ** This method is used to define how the resource server sends a request to the remote authentication server for token verification * @param resources * @throws Exception * / @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("autodeliver"); // Define token service objects (token verification should depend on token service objects) RemoteTokenServices remoteTokenServices = new RemoteTokenServices(); // Validates endpoint/interface Settings remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9998/oauth/check_token"); // Carry the client ID and client security code remoteTokenServices.setClientId("client_lagou"); remoteTokenServices.setClientSecret("abcxyz"); // Don't forget this step resources.tokenServices(remoteTokenServices); } / * ** Scenario: There may be many resources in a service (API interface)* Some API interfaces need authentication before they can be accessed* Some API interfaces do not require authentication at all, and are inherently open interfaces* We need to differentiate between different interfaces (done in the current configure method) and set themWhether authentication is required * * @param http * @throws Exception * / @Override public void configure(HttpSecurity http) throws Exception { http // Set the session creation policy. .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .authorizeRequests() // Requests prefixed with autoDeliver require authentication .antMatchers("/autodeliver/**").authenticated() // Requests prefixed with demo require authentication .antMatchers("/demo/**").authenticated() .anyRequest().permitAll(); // Other requests are not authenticated } } Copy the code
Thinking: when we log in for the first time, the authentication server issues the token and stores it in the authentication server. Later, when we visit the resource server, we carry the token, and the resource server will request the authentication server to verify the validity of the token. If there are many resource servers, the authentication server will be under great pressure…….
In addition, the resource server obtains the user information UserInfo from the authentication server check_token, whether the user information can be stored in the token, so that the client always hold the token, token verification is also carried out in the resource server, so as to avoid frequent interaction with the authentication server……
We can consider using JWT for modification. With JWT, the resource server does not need to access the authentication server……
5. JWT changes the token storage mechanism of the unified Certification and Authorization Center
5.1 INTRODUCTION to JWT Token:
Through the above test, we found that when resource service and authorization service are not together, resource service uses RemoteTokenServices to remotely request authorization service to verify token. If the number of visits is large, the system performance will be affected. Solve the above problems: Token by JWT format can solve the above problems, the user authentication through a JWT will get tokens, JWT is included in the token user related information, the client only need to carry JWT access resources service, resource service according to the predetermined algorithm itself token check, without having to request certification service every time to complete the authorization.
5.2 What is JWT?
JSON Web Token (JWT) is an open industry standard (RFC 7519) that defines a brief, self-contained protocol format for transferring JSON objects between communication parties that can be digitally signed to be verified and trusted. JWT can be signed using the HMAC algorithm or using RSA’s public/private key pair to prevent tampering.
5.3 JWT token structure
The JWT token consists of three parts, each with a dot (.) in the middle. Separate, for example: xxXXX.yyyyy.zzzzz
- Header: The Header contains the type of the token (that is, JWT) and the hash algorithm used (such as HMAC SHA256 or RSA), for example
{
"alg": "HS256"."typ": "JWT"
}
Copy the code
Encoding the above content using Base64Url to get a string is the first part of the JWT token.
- Payload The second part of the Payload is a JSON object that stores valid information. It can store ready-made fields provided by JWT, such as iss (signer),exp (expiration timestamp), and sub (user). You can also customize fields. This section is not recommended for storing sensitive information because it can be decoded to restore the original content. Finally, the second part of the payload is encoded using Base64Url, resulting in a string that is the second part of the JWT token. An example:
{
"sub": "1234567890"."name": "John Doe"."iat": 1516239022
}
Copy the code
- The third part of Signature is the Signature, which is used to prevent JWT content from being tampered with. This part encodes the first two parts using base64URL, using the dot (.). The connection is a string, and the signature algorithm is declared in the header.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Copy the code
- base64UrlEncode(header) : the first part of the JWT token. - base64UrlEncode(Payload) : the second part of the JWT token. - secret: indicates the key used for signing.Copy the code
5.4 Code Modification (Authentication Server and Resource Server)
Authentication server side JWT modification (Modification master configuration class)
- Modify the media storing the token to JWT, and configure the converter to convert the information to JWT token
/ * ** This method is used to create tokenStore objects (token store objects) in what form are tokens stored* /
public TokenStore tokenStore(a){
//return new InMemoryTokenStore();
// Use the JWT token return new JwtTokenStore(jwtAccessTokenConverter()); } private String sign_key = "xxxxx"; // JWT signature key / * ** Returns the JWT token converter (which helped us generate the JWT token)* Here, we can pass in the signing key to the converter object * @return * / private JwtAccessTokenConverter jwtAccessTokenConverter(a) { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); // Signature key jwtAccessTokenConverter.setSigningKey(sign_key); // The key used for authentication must be the same as the signature key jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); return jwtAccessTokenConverter; } Copy the code
- Modify the JWT token service method, primarily by adding a JWT converter
The resource server validates the JWT token
- Instead of interacting with a remote authentication server, add a local tokenStore and change the configure method
/ * ** This method is used to define how the resource server sends a request to the remote authentication server for token verification * @param resources
* @throws Exception
* /
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { // resources.resourceId("autodeliver"); // // Define token service objects (token verification should depend on token service objects) // RemoteTokenServices remoteTokenServices = new RemoteTokenServices(); // // Verifies endpoint/interface Settings // remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9998/oauth/check_token"); // // Contains the client ID and client security code // remoteTokenServices.setClientId("client_lagou"); // remoteTokenServices.setClientSecret("abcxyz"); // // Don't forget this step // resources.tokenServices(remoteTokenServices); // JWT token modification resources.resourceId("autodeliver").tokenStore(tokenStore()).stateless(true); } Copy the code
- Add and authentication server just ask him token store
/ * ** This method is used to create tokenStore objects (token store objects) in what form are tokens stored* /
public TokenStore tokenStore(a){
//return new InMemoryTokenStore();
// Use the JWT token return new JwtTokenStore(jwtAccessTokenConverter()); } private String sign_key = "190coder.cn"; // JWT signature key / * ** Returns the JWT token converter (which helped us generate the JWT token)* Here, we can pass in the signing key to the converter object * @return * / private JwtAccessTokenConverter jwtAccessTokenConverter(a) { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); // Signature key jwtAccessTokenConverter.setSigningKey(sign_key); // The key used for authentication must be the same as the signature key jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); return jwtAccessTokenConverter; } Copy the code
6. Load Oauth2 client information from the database
- Create and initialize the data table. Data is available from source code (table names and fields remain fixed)
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
Configuring a Data Source-- Table structure for oauth_client_details
-- ---------------------------- DROP TABLE IF EXISTS `oauth_client_details`; CREATE TABLE `oauth_client_details` ( `client_id` varchar(48) NOT NULL.`resource_ids` varchar(256) DEFAULT NULL.`client_secret` varchar(256) DEFAULT NULL.`scope` varchar(256) DEFAULT NULL.`authorized_grant_types` varchar(256) DEFAULT NULL.`web_server_redirect_uri` varchar(256) DEFAULT NULL.`authorities` varchar(256) DEFAULT NULL.`access_token_validity` int(11) DEFAULT NULL.`refresh_token_validity` int(11) DEFAULT NULL.`additional_information` varchar(4096) DEFAULT NULL.`autoapprove` varchar(256) DEFAULT NULL.PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of oauth_client_details -- ---------------------------- BEGIN; INSERT INTO `oauth_client_details` VALUES ('client_lagou123'.'autodeliver,resume'.'abcxyz'.'all'.'password,refresh_token'.NULL.NULL.7200.259200.NULL.NULL); COMMIT; SET FOREIGN_KEY_CHECKS = 1; Copy the code
- Configuring a Data Source
Spring:
application:
name: test-oauth2-9998
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/lagou_homework? useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true username: root password: 123321 druid: initialSize: 10 minIdle: 10 maxActive: 30 maxWait: 50000 Copy the code
- The primary configuration of the authentication server was modified
/ * ** Customer review configuration,* For example client_id, secret* At present, this service is just like QQ platform. As a client, Dragnet needs QQ platform for login authorization and authentication, which is required in advanceTo register QQ platform, QQ platform will give the hook net* Issue necessary parameters such as client_id to indicate who the client is * * @param clients * @throws Exception * * / @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { super.configure(clients); // clients. Where is the // // client information stored, either in memory or in a database // inMemory() // // Add a client configuration specifying its client_id // .withClient("client_lagou") // // Specifies the password and security code of the client // .secret("abcxyz") // // Specifies the resources that the client can access // .resourceIds("autodeliver") // // Authentication type/token issuing mode. Multiple authentication types and token issuing modes can be configured here, but not all of them can be used. The specific token issuing mode needs to be specified when the client calls // .authorizedGrantTypes("password","refresh_token") // // Client permission range. Set this parameter to all // .scopes("all"); // Load the client view from memory instead of the database clients.withClientDetails(createJdbcClientDetailsService()); } @Autowired private DataSource dataSource; @Bean public JdbcClientDetailsService createJdbcClientDetailsService(a) { JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource); return jdbcClientDetailsService; } Copy the code
7. Verify the user validity from the database
- Create table Users (table name is not fixed) and initialize the data
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` char(10) DEFAULT NULL.`password` char(100) DEFAULT NULL.PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of users -- ---------------------------- BEGIN; INSERT INTO `users` VALUES (4.'lagou-user'.'iuxyzds'); COMMIT; SET FOREIGN_KEY_CHECKS = 1; Copy the code
- Develop the implementation class of the UserDetailsService interface, and load the user information from the database according to the user name
/ * * * @author: 190coder <190coder.cn>
* @description: Implementation class of the UserDetailsService interface * @create: 2020-07-30"* /
@Service public class JdbcUserDetailsService implements UserDetailsService { / / Jpa query @Autowired private UsersRepository usersRepository; / * ** Query all information about the user according to username, encapsulate the object as UserDetails, as the password, box* Frames will automatically match * * @param s * @return * @throws UsernameNotFoundException * / @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { Users users = usersRepository.findByUsername(s); return new User(users.getUsername(),users.getPassword(),new ArrayList<>()); } } Copy the code
- Using custom user details service objects (authentication configuration classes)
@Autowired
private JdbcUserDetailsService jdbcUserDetailsService;
/ * * *
* Handle username and password authentication* 1) The client passes username and password parameters to the authentication server* 2) In general, username and password are stored in the user table in the database* 3) Verify the validity of the user information currently transmitted according to the data in the user table * * / @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // In this method, we can disassociate the database. For now, we can configure the user information in memory // Instantiate a user object (equivalent to a user record in a data table) // UserDetails user = new User("admin","123456",new ArrayList<>()); // auth.inMemoryAuthentication() // .withUser(user).passwordEncoder(passwordEncoder); auth.userDetailsService(jdbcUserDetailsService).passwordEncoder(passwordEncoder); } Copy the code
8. JWT token information extension based on Oauth2
OAuth2 helped us generate the JWT token payload with limited information, only one user_name for the user information, in some cases we want to add some extended information items such as IP (for security), UserId… Add the extended information as follows:
The authentication server stores extended information (such as clientIp) when generating JWT tokens
- Inheritance DefaultAccessTokenConverter class, rewrite convertAccessToken method in the extension information
/ * * * @author: 190coder <190coder.cn>
* @description: User-defined extended information * @create: the 2020-07-30 suffering justly* /
public class LagouAccessTokenConvertor extends DefaultAccessTokenConverter { @Override publicMap<String, ? > convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { // Get the request object HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest(); // Get the client IP (note: this is not the real browser client IP if the server is reached through the proxy) String remoteAddr = request.getRemoteAddr(); Map<String, String> stringMap = (Map<String, String>) super.convertAccessToken(token, authentication); stringMap.put("clientIp",remoteAddr); return stringMap; } } Copy the code
- Inject the custom converter object into the class returned by the configuration JWT
/ * ** Returns the JWT token converter (which helped us generate the JWT token)* Here, we can pass in the signing key to the converter object * @return
* /
public JwtAccessTokenConverter jwtAccessTokenConverter(a) { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); jwtAccessTokenConverter.setSigningKey(sign_key); // Signature key jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // The key used for authentication must be the same as the signature key jwtAccessTokenConverter.setAccessTokenConverter(lagouAccessTokenConvertor); return jwtAccessTokenConverter; } Copy the code
9. The resource server retrieves the JWT token extension information
-
Resource server also needs to customize a converter class, inheritance DefaultAccessTokenConverter, rewrite extractAuthentication extraction method, the load information is set to the authentication details of an object attribute
-
Method to inject a custom converter object that returns a JWT token converter
-
Business class such as the Controller class, can pass SecurityContextHolder. GetContext () getAuthentication () to get to the authentication object, further to the extension information
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
Copy the code
- Once you get the extended information, you can do other processing, such as further processing according to userId, or processing according to clientIp, or whatever
10. Precautions
- JWT tokens are a verifiable data organization format that can be used to create and validate JWT tokens based on Spring Cloud Oauth2
- We can also write our own utility classes to generate and validate JWT tokens
- Don’t store overly sensitive information in the JWT token because we know that once we get the token, we can decode and see the payload
- JWT tokens are carried on each request and too much content can increase network bandwidth usage
Lagouedu should top big guy notes collation
This article is formatted using MDNICE