【十二】统⼀认证方案 Spring Cloud OAuth2+JWT

上篇【十一】统⼀认证方案 Spring Cloud OAuth2

什么是JWT

JSON Web Token(JWT)是⼀个开放的行业标准(RFC 7519),它定义了⼀种简介的、自包含的协议格式,用于 在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使⽤RSA的公 钥/私钥对来签名,防止被篡改。

JWT令牌结构 JWT

在这里插入图片描述

JWT令牌由三部分组成,每部分中间使用点(.)分隔,如:xxxxx.yyyyy.zzzzz

  • Header
    头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA),例如将上边的内容使用Base64Url编码,得到⼀个字符串就是JWT令牌的第⼀部分。
  • Payload
    第⼆部分是负载,内容也是⼀个json对象,它是存放有效信息的地⽅,它可以存放jwt提供的现成字段, 如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。 此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。 最后将第⼆部分负载使用Base64Url编码,得到⼀个字符串就是JWT令牌的第⼆部分。
  • Signature
    第三部分是签名,此部分用于防jwt内容被篡改。 这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。

服务改造

  • 认证服务器端JWT改造(改造主配置类)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;
    }
  • 资源服务器校验JWT令牌m-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;
    }
}
  • 测试
    1. 获取Token
http://localhost:9999/oauth/token?client_secret=abcdef&grant_type=password&username=admin&password=123456&client_id=client_w

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述
4. 访问服务

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

在这里插入图片描述

从数据库加载Oauth2客户端信息

m-cloud-oauth-server-9999服务改造

  • 新建数据库oauth2,初始化脚本
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;
  • 配置数据源
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
  • 依赖
 <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>
  • 认证服务器主配置类改造OauthServerConfiger
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;
    }
}

  • 重启验证
http://localhost:9999/oauth/token?client_secret=abcdef&grant_type=password&username=admin&password=123456&client_id=client_w123

在这里插入图片描述

从数据库验证用户合法性

  1. m-service-common 服务新建pojo
@Data
@Entity
@Table(name = "users")
@Accessors
public class Users {
    
    
    @Id
    private Long id; // 主键
    private String username;
    private String password;
}
  1. 改造服务m-cloud-oauth-server-9999
    在这里插入图片描述
  • 添加依赖
<!--添加对common⼯程的依赖-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>m-service-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
  • 新建dao
public interface UsersRespostory extends JpaRepository<Users,Long> {
    
    
    public Users findByUsername(String username);
}
  • 新建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 引入JdbcUserDetailsService 并修改configure方法
@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. 验证
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

基于Oauth2的 JWT 令牌信息扩展

  • 认证服务–令牌信息添加额外的ip信息
  1. 改造m-cloud-oauth-server-9999 添加新的config配置类
@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. 修改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. 重启验证
    在这里插入图片描述
    在这里插入图片描述
  • 资源服务-获取ip信息m-service-autodeliver-8092
  1. 获取ip信息
@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. 起服务验证
    在这里插入图片描述
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u014535922/article/details/130158551
今日推荐