【12】Unified authentication solution Spring Cloud OAuth2+JWT

Previous article [11] Unified authentication solution Spring Cloud OAuth2

What is JWT

JSON Web Token (JWT) is an open industry standard (RFC 7519), which defines a brief, self-contained protocol format for transferring json objects between communicating parties. The transferred information can be verified through digital signatures and trust. JWT can be signed using the HMAC algorithm or the RSA public/private key pair to prevent tampering.

JWT Token Structure JWT

Insert image description here

The JWT token consists of three parts, each part is separated by a dot (.), such as: xxxxx.yyyyy.zzzzz

  • The Header
    header includes the type of token (i.e. JWT) and the hash algorithm used (such as HMAC SHA256 or RSA). For example, use Base64Url to encode the above content and obtain a string that is the first part of the JWT token.
  • The second part of Payload
    is the payload, and the content is also a json object. It is a place to store valid information. It can store ready-made fields provided by jwt, such as: iss (issuer), exp (expiration timestamp), sub (for users), etc., you can also customize fields. It is not recommended to store sensitive information in this section because this section can be decoded to restore the original content. Finally, use Base64Url to encode the second part of the payload to obtain a string that is the second part of the JWT token.
  • The third part of Signature
    is the signature, which is used to prevent the jwt content from being tampered with. This part uses base64url to encode the first two parts. After encoding, use dots (.) to connect to form a string. Finally, use the signature algorithm declared in the header to sign.

Service transformation

  • Authentication server-side JWT transformation (renovation of the main configuration class)m-cloud-oauth-server-9999
    • OauthServerConfiger
private String sign_key="w123";
//***************************************************************省略***************
 /**
     * Token 存储方式
     *
     * @return
     */
    public TokenStore tokenStore() {
    
    
//        return new InMemoryTokenStore();
        return  new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * Jwt令牌
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
    
    
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));//对称加密
        return jwtAccessTokenConverter;
    }
 /**
     * 获取Token服务对象,描述token有效期等信息
     *
     * @return
     */
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
    
    
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //开启令牌刷新
        defaultTokenServices.setSupportRefreshToken(Boolean.TRUE);
        //
        defaultTokenServices.setTokenStore(tokenStore());
        //针对jwt令牌的添加
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
        //设置令牌有效时间(一半2小时)
        defaultTokenServices.setAccessTokenValiditySeconds(20);
        //设置令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(259200);
        return defaultTokenServices;
    }
  • Resource server verifies JWT tokenm-service-autodeliver-8092
    • ResourceServerConfiger
@Configuration
public class ResourceServerConfiger extends ResourceServerConfigurerAdapter {
    
    
    //jwt 签名秘钥
    private String sign_key = "w123";

    /**
     * 用于定义资源服务器向远程认证服务器发起请求,进行token 校验
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    
    
  /*      //资源id
        resources.resourceId("autodeliver");
        //Token 服务对象
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        //校验端口
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
        //客户端id和客户端密码
        remoteTokenServices.setClientId("client_w");
        remoteTokenServices.setClientSecret("abcdef");
        resources.tokenServices(remoteTokenServices);*/
        //TODO JWT 令牌改造
        resources
                .resourceId("autodeliver")
                .tokenStore(tokenStore())
                .stateless(true);

    }

    /**
     * 对不同接口认证授权访问
     * 场景:一个服务中有很多资源(api接口)
     * 某些接口,需要先认证,才能访问
     * 某些接口,不需要认证,本来就是对外访问的接口
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
    
    
        //设置session创建策略
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .authorizeRequests()
                .antMatchers("/autodeliver/**").authenticated()
                .antMatchers("/demo/**").authenticated()
                .anyRequest().permitAll();
    }

    /**
     * Token 存储方式
     *
     * @return
     */
    public TokenStore tokenStore() {
    
    
//        return new InMemoryTokenStore();
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * Jwt令牌
     *
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
    
    
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));//对称加密
        return jwtAccessTokenConverter;
    }
}
  • test
    1. Get Token
http://localhost:9999/oauth/token?client_secret=abcdef&grant_type=password&username=admin&password=123456&client_id=client_w

Insert image description here

  1. Verify Token
http://localhost:9999/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwiZXhwIjoxNjgxNDY0MDc0LCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6IjY3ZDQ5YTRmLTBkNWMtNGFkNy05NjAzLTVmMmVkNGFhOTc1MiIsImNsaWVudF9pZCI6ImNsaWVudF93Iiwic2NvcGUiOlsiYWxsIl19.ENCxdiB47dFm2zwnu8Ka8-utWgA3p0wExHgtpf65xgU

Insert image description here

  1. Refresh token
http://localhost:9999/oauth/token?grant_type=refresh_token&client_id=client_w&client_secret=abcdef&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiOGY1ZDdmOWYtYWJkYS00ZDQxLTg1YWMtMTMyMTI0ZmI3ZTA2IiwiZXhwIjoxNjgxNzI0MzU4LCJqdGkiOiIyZDM3YWE3ZC05YzNlLTRjZGEtYmU4My00ZjA4ODAwY2I5YjMiLCJjbGllbnRfaWQiOiJjbGllbnRfdyJ9.KovQ88Pvb4WxaixUVzp0Ft0dYhnAelQVaWhihX3ipOQ

Insert image description here
4. ACCESS TO SERVICES

http://localhost:8092/autodeliver/checkState/1545133?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXV0b2RlbGl2ZXIiXSwiZXhwIjoxNjgxNDY1MjU0LCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6ImY3MDBiOTY2LTM1Y2EtNDI4Yy1hZjhjLWIwNTA4OWI1ZTBiYiIsImNsaWVudF9pZCI6ImNsaWVudF93Iiwic2NvcGUiOlsiYWxsIl19.Wr4liMiJabZkwmRRadgB7GFhhSAQaGYAC2UP60AJ560

Insert image description here

Load Oauth2 client information from database

m-cloud-oauth-server-9999Service transformation

  • Create a new database oauth2and initialize the script
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- 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_w123',
'autodeliver,resume', 'abcdef', 'all', 'password,refresh_token', NULL, NULL,
7200, 259200, NULL, NULL);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
  • Configure data source
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/oauth2?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    druid:
      initialSize: 10
      minIdle: 10
      maxActive: 30
      maxWait: 50000
  • rely
 <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
  • Authentication server main configuration class modificationOauthServerConfiger
package com.w.edu.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.jwt.crypto.sign.MacSigner;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
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.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

/**
 * @author Mrwg
 * @date 2023/4/13 19:30
 * @description
 */
@Configuration
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {
    
    

    private String sign_key="w123";

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;

    /**
     * 认证服务器最终以api接口方式对外提供服务(生成令牌、校验令牌)
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    
    
        super.configure(security);
        security
                //允许客户端表单认证
                .allowFormAuthenticationForClients()
                //开启端口/oauth/token_key的访问权限
                .tokenKeyAccess("permitAll()")
                //开启端口/oauth/check_token的访问权限
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端吧详情配置
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    
        super.configure(clients);
     /*   //客户端信息存储在什么地方,既可以在内存也可以在数据库里
        clients.inMemory()
                //添加客户端配置。指定client_id
                .withClient("client_w")
                //指定客户端密码、安全码
                .secret("abcdef")
                //指定客户端可访问的资源id 清单
                .resourceIds("autodeliver")
                //认证类型、客户端令牌颁发模式//认证模式。可以配置多个
                .authorizedGrantTypes("password", "refresh_token")
                //客户端权限范围,配置all 即可
                .scopes("all");*/

        clients.withClientDetails(jdbcClientDetailsService());

    }

    public JdbcClientDetailsService jdbcClientDetailsService(){
    
    
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        return jdbcClientDetailsService;
    }

    /**
     * Token 令牌管理相关
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    
    
        super.configure(endpoints);
        endpoints
                //token 存储方式
                .tokenStore(tokenStore())
                //配置token细节
                .tokenServices(authorizationServerTokenServices())
                //认证管理器
                .authenticationManager(authenticationManager)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }

    /**
     * Token 存储方式
     *
     * @return
     */
    public TokenStore tokenStore() {
    
    
//        return new InMemoryTokenStore();
        return  new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * Jwt令牌
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
    
    
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));//对称加密
        return jwtAccessTokenConverter;
    }

    /**
     * 获取Token服务对象,描述token有效期等信息
     *
     * @return
     */
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
    
    
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //开启令牌刷新
        defaultTokenServices.setSupportRefreshToken(Boolean.TRUE);
        //
        defaultTokenServices.setTokenStore(tokenStore());
        //针对jwt令牌的添加
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
        //设置令牌有效时间(一半2小时)
        defaultTokenServices.setAccessTokenValiditySeconds(20);
        //设置令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(259200);
        return defaultTokenServices;
    }
}

  • Restart verification
http://localhost:9999/oauth/token?client_secret=abcdef&grant_type=password&username=admin&password=123456&client_id=client_w123

Insert image description here

Verify user legitimacy from database

  1. m-service-commonService new pojo
@Data
@Entity
@Table(name = "users")
@Accessors
public class Users {
    
    
    @Id
    private Long id; // 主键
    private String username;
    private String password;
}
  1. Remodeling servicesm-cloud-oauth-server-9999
    Insert image description here
  • Add dependencies
<!--添加对common⼯程的依赖-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>m-service-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
  • Create new dao
public interface UsersRespostory extends JpaRepository<Users,Long> {
    
    
    public Users findByUsername(String username);
}
  • Create new service
@Service
public class JdbcUserDetailsService implements UserDetailsService {
    
    
    @Autowired
    private UsersRespostory usersRespostory;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
    
        Users users = usersRespostory.findByUsername(username);
        return new User(users.getUsername(), users.getPassword(), new ArrayList<>());
    }
}
  1. SecurityConfiger introduces JdbcUserDetailsService and modifies the configure method
@Configuration
public class SecurityConfiger extends WebSecurityConfigurerAdapter {
    
    

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JdbcUserDetailsService jdbcUserDetailsService;
    /**
     *注入AuthenticationManager
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
    
    
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
    
    
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 处理用户名密码事宜
     * 1 客户传递username 和password 参数到认证服务器
     * 2一半存储在数据表中
     * 3 根据用户传递的数据,验证当前传递过来用户信息的合法性
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
       // 实例化用户对象
//        UserDetails user = new User("admin","123456",new ArrayList<>());
//        auth.inMemoryAuthentication()
//                .withUser(user).passwordEncoder( passwordEncoder());
        auth.userDetailsService(jdbcUserDetailsService).passwordEncoder(passwordEncoder);
    }
}

  1. verify
    Insert image description here
    Insert image description here

Insert image description here

JWT token information extension based on Oauth2

  • Authentication service – token information adds additional ip information
  1. Transformation m-cloud-oauth-server-9999Add new config configuration class
@Component
public class MDefaultAccessTokenConverter extends DefaultAccessTokenConverter {
    
    
    @Override
    public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
    
    
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //客户端ip
        String remoteAddr = request.getRemoteAddr();
        Map<String, String> stringMap = (Map<String, String>) super.convertAccessToken(token, authentication);
        stringMap.put("clientIp",remoteAddr);
        return stringMap;
//        return super.convertAccessToken(token, authentication);
    }
}
  1. Modify OauthServerConfiger.java
@Configuration
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {
    
    

   
    @Autowired
    private  MDefaultAccessTokenConverter defaultAccessTokenConverter;

   //******************************略部分代码*********
    /**
     * Jwt令牌
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
    
    
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));//对称加密
        //新增
        jwtAccessTokenConverter.setAccessTokenConverter(defaultAccessTokenConverter);
        return jwtAccessTokenConverter;
    }

  
}
  1. Restart verification
    Insert image description here
    Insert image description here
  • Resource service-get ip informationm-service-autodeliver-8092
  1. Get ip information
@Component
public class MDefaultAccessTokenConverter extends DefaultAccessTokenConverter {
    
    
    //令牌信息提取
    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
    
    
        OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map);
        oAuth2Authentication.setDetails(map); //放入认证对象中
        return oAuth2Authentication;
    }
}
@Configuration
public class ResourceServerConfiger extends ResourceServerConfigurerAdapter {
    
    
//**************************************略部分代码***************

    @Autowired
    private MDefaultAccessTokenConverter defaultAccessTokenConverter;

   
    /**
     * Jwt令牌
     *
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
    
    
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key); //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));//对称加密
        //新增
        jwtAccessTokenConverter.setAccessTokenConverter(defaultAccessTokenConverter);
        return jwtAccessTokenConverter;
    }
}
  1. Start service verification
    Insert image description here
    Insert image description here

Guess you like

Origin blog.csdn.net/u014535922/article/details/130158551