首页 > 其他分享 >SpringSecurity -- 授权-OAuth2 --

SpringSecurity -- 授权-OAuth2 --

时间:2023-08-10 19:00:55浏览次数:77  
标签:OAuth2 varchar -- SpringSecurity DEFAULT token NULL 256 public

前言

针对之前的授权做个补充,这里集成OAuth2来实现

目前支持5种方式,inMemory,jdbc,redis,jwt,jwk

对于公司内部使用呢,主推 jwt > redis > jdbc > inMemory

​ 。。。jwk 没玩明白,但是安全性挺高。。。整明白了,觉得可以主推!!!

而认证类型呢,也有5种,authorization_code,refresh_token,password,implicit,client_credentials

对于公司内部,主推 password > authorization_code,

refresh_token一般都是会采用的,所以不区分优先级,后面两种非常的不安全,直接不推荐了

授权服务器-核心代码

0. 在启动类上加注解

@SpringBootApplication
@EnableAuthorizationServer // 开启授权服务器注解
public class AuthApplication {
    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class);
    }
}

1. SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean // 暴漏 manager 给其它类使用
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager user = new InMemoryUserDetailsManager();
        // 使用内存认证,必须指定角色(roles),实际使用中,肯定是需要自定义对接数据库的,这里为了演示
        user.createUser(User.withUsername("a").password(passwordEncoder().encode("123")).roles("ADMIN").build());
        return user;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and().formLogin()
                .and().csrf().disable();
    }
}

ps: 以下4中Config只能同时采用1种,切记 ps: 以下4中Config只能同时采用1种,切记 ps: 以下4中Config只能同时采用1种,切记

2. InMemoryAuthServerConfig

@Configuration
public class InMemoryAuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    UserDetailsService detailsService;
    @Resource
    PasswordEncoder encoder;
    @Resource
    AuthenticationManager manager;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用内存模式
        clients.inMemory()
                // 创建client为 a, secret 为 123, 这里框架要求secret必须强制加密,否则会报错
                .withClient("a").secret(encoder.encode("123"))
                // 权限,all所有, read, write
                .scopes("all")
                // 自动授权
                .autoApprove("all")
                // 转发跳转URL
                .redirectUris("https://www.baidu.com")
                // 认证类型 包含
                .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue(), // authorization_code
                        AuthorizationGrantType.REFRESH_TOKEN.getValue(), // refresh_token
                        AuthorizationGrantType.IMPLICIT.getValue(), // implicit
                        AuthorizationGrantType.PASSWORD.getValue(), // password
                        AuthorizationGrantType.CLIENT_CREDENTIALS.getValue() // client_credentials
                );
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.userDetailsService(detailsService);// 开启刷新令牌必须指定, 它需要后台自动去认证
        endpoints.authenticationManager(manager); // 使用密码模式必须指定 manager,它需要后台自动去认证
    }
}

3. JdbcAuthServerConfig

@Configuration
public class JdbcAuthServerConfig extends AuthorizationServerConfigurerAdapter {


    @Resource
    PasswordEncoder encoder;
    @Resource
    AuthenticationManager manager;
    @Resource
    DataSource dataSource;

    @Bean // 配置token存储数据源
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean  // 声明ClientDetailsService,配置数据源 以及 加密方式
    public ClientDetailsService clientDetails(){
        JdbcClientDetailsService service = new JdbcClientDetailsService(dataSource);
        service.setPasswordEncoder(encoder);
        return service;
    }

    @Override // 配置 客户端 采用jdbc  模式,
    // 使用 jdbc() ,不能直接这么写 clients.jdbc(dataSource)  因为secret经过bcrypt加密,所以要指定加密方式
    // 所以要自定义 clientDetails
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());
    }

    @Override // 配置认证端点
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(manager);//认证管理器
        endpoints.tokenStore(tokenStore());//配置令牌存储为数据库存储

        // 配置TokenServices参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();//修改默认令牌生成服务
        tokenServices.setTokenStore(endpoints.getTokenStore());//基于数据库令牌生成
        tokenServices.setSupportRefreshToken(true);//是否支持刷新令牌
        tokenServices.setReuseRefreshToken(true);//是否重复使用刷新令牌(直到过期)

        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());//设置客户端信息
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());//用来控制令牌存储增强策略
        //访问令牌的默认有效期(以秒为单位)。过期的令牌为零或负数。
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天
        //刷新令牌的有效性(以秒为单位)。如果小于或等于零,则令牌将不会过期
        tokenServices.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(3)); //3天
        endpoints.tokenServices(tokenServices);//使用配置令牌服务
    }
}
初始化脚本
SET NAMES utf8mb4;
SET
FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for clientdetails
-- ----------------------------
DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE `clientdetails`
(
    `appId`                  varchar(256) NOT NULL,
    `resourceIds`            varchar(256)  DEFAULT NULL,
    `appSecret`              varchar(256)  DEFAULT NULL,
    `scope`                  varchar(256)  DEFAULT NULL,
    `grantTypes`             varchar(256)  DEFAULT NULL,
    `redirectUrl`            varchar(256)  DEFAULT NULL,
    `authorities`            varchar(256)  DEFAULT NULL,
    `access_token_validity`  int(11) DEFAULT NULL,
    `refresh_token_validity` int(11) DEFAULT NULL,
    `additionalInformation`  varchar(4096) DEFAULT NULL,
    `autoApproveScopes`      varchar(256)  DEFAULT NULL,
    PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`
(
    `token_id`          varchar(256) DEFAULT NULL,
    `token`             blob,
    `authentication_id` varchar(256) NOT NULL,
    `user_name`         varchar(256) DEFAULT NULL,
    `client_id`         varchar(256) DEFAULT NULL,
    `authentication`    blob,
    `refresh_token`     varchar(256) DEFAULT NULL,
    PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals`
(
    `userId`         varchar(256)       DEFAULT NULL,
    `clientId`       varchar(256)       DEFAULT NULL,
    `scope`          varchar(256)       DEFAULT NULL,
    `status`         varchar(10)        DEFAULT NULL,
    `expiresAt`      timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `lastModifiedAt` date               DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`
(
    `client_id`               varchar(256) 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=utf8mb4;

-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token`
(
    `token_id`          varchar(256) DEFAULT NULL,
    `token`             blob,
    `authentication_id` varchar(256) NOT NULL,
    `user_name`         varchar(256) DEFAULT NULL,
    `client_id`         varchar(256) DEFAULT NULL,
    PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code`
(
    `code`           varchar(256) DEFAULT NULL,
    `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`
(
    `token_id`       varchar(256) DEFAULT NULL,
    `token`          blob,
    `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

SET
FOREIGN_KEY_CHECKS = 1;

-- 写入客户端信息
-- client_id 为 a
-- client_secret 为 123
INSERT INTO `oauth_client_details`
VALUES ('a', NULL, '$2a$10$tbFi2xQwHQcjIp4jt5eGZ..CER7ws.Pzg9RiBM1tTGH1omkNprXqC', 'all',
        'authorization_code,refresh_token,password,client_credentials', 'https://www.baidu.com', NULL, NULL, NULL, NULL, NULL);

4. RedisAuthServerConfig

@Configuration
public class RedisAuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    private PasswordEncoder encoder;
    @Resource
    private AuthenticationManager manager;
    @Resource
    UserDetailsService detailsService;

    final RedisConnectionFactory factory;

    public RedisAuthServerConfig(RedisConnectionFactory factory) {
        this.factory = factory;
    }

    @Override //配置使用 redis 方式颁发令牌,同时配置 redis 转换器
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore())  // 设置令牌生成方式
                .accessTokenConverter(converter())  // 设置令牌转换器
                .authenticationManager(manager)  // 使用 AuthenticationManager 管理
                .userDetailsService(detailsService); // 刷新令牌必须使用
    }

    @Bean //使用redis方式生成令牌
    public TokenStore tokenStore() {
        // 设置redis存储
        return new RedisTokenStore(factory);
    }

    @Bean// 使用默认的转换器
    public DefaultAccessTokenConverter converter() {
        return new DefaultAccessTokenConverter();
    }

    @Override//使用内存方式
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 设置 clientDetails
        clients.inMemory().withClient("a").secret(encoder.encode("123"))
                .scopes("all")
                .autoApprove("all")
                .redirectUris("https://www.baidu.com")
                .authorizedGrantTypes("authorization_code", "refresh_token", "implicit", "password", "client_credentials");
    }
}

5. JwtAuthServerConfig

@Configuration
public class JwtAuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    private PasswordEncoder encoder;
    @Resource
    private AuthenticationManager manager;
    @Resource
    private DataSource dataSource;

    @Override //配置使用 jwt 方式颁发令牌,同时配置 jwt 转换器
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore())  // 设置令牌生成方式
                .accessTokenConverter(converter())  // 设置jwt令牌转换器
                .authenticationManager(manager);  // 使用 AuthenticationManager 管理
    }

    @Bean //使用JWT方式生成令牌
    public TokenStore tokenStore() {
        // 设置jwt令牌转换器
        return new JwtTokenStore(converter());
    }

    @Bean//使用同一个密钥来编码 JWT 中的  OAuth2 令牌
    public JwtAccessTokenConverter converter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");//可以采用属性注入方式 生产中建议使用复杂字符串,或者字符串加密,或者加盐
        return converter;
    }

    @Override//使用数据库方式客户端存储
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 设置 clientDetails
        // 默认是 inMemory() ,基于内存
        // 如果使用 jdbc() ,不能直接这么写 clients.jdbc(dataSource)  因为secret经过bcrypt加密,所以要指定加密方式
        // 即需要声明 ClientDetails
        clients.withClientDetails(clientDetails());
    }

    @Bean // 声明 ClientDetails实现
    public ClientDetailsService clientDetails() {
        // 设置数据源
        JdbcClientDetailsService service = new JdbcClientDetailsService(dataSource);
        // 设置加密方式
        service.setPasswordEncoder(encoder);
        return service;
    }
}

资源服务器-核心代码

@Configuration
@EnableResourceServer // 开启资源服务器(也可以放在启动类上)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    final DataSource dataSource;
    final RedisConnectionFactory factory;

    @Autowired
    public ResourceServerConfig(DataSource dataSource, RedisConnectionFactory factory) {
        this.dataSource = dataSource;
        this.factory = factory;
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore());
    }

    @Bean//使用同一个密钥来编码 JWT 中的  OAuth2 令牌
    public JwtAccessTokenConverter converter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");//可以采用属性注入方式 生产中建议使用复杂字符串,或者字符串加密,或者加盐
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        // 基于内存
        // 但是呢,如果授权服务器和资源服务器在一起,那么授权服务器需要登录,然后获得授权信息,
        // 此时访问同一服务下的资源,也是可以ok的,所以在这种情况下,这里就没必要了
        return new InMemoryTokenStore();
        // 基于JWT
//        return new JwtTokenStore(converter());
        // 基于数据库
//        return new JdbcTokenStore(dataSource);
        // 基于redis
//        return new RedisTokenStore(factory);
    }

    // 当然,也可以直接发http请求的方式校验
    //    ## OAuth2 标准接口
    //- /oauth/authorize:授权端点

    //- /oauth/token:获取令牌端点

    //- /oauth/confirm_access:用户确认授权提交端点

    //- /oauth/error:授权服务错误信息端点

    //- /oauth/check_token:用于资源服务访问的令牌解析端点

    //- /oauth/token_key:提供公有密匙的端点,如果使用JWT令牌的话
}

pom

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.2.5.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 引入授权服务器依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <!-- 引入资源服务器依赖 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-resource-server</artifactId>
        </dependency>

        <!--引入 jdbc 依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--引入 redis 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

标签:OAuth2,varchar,--,SpringSecurity,DEFAULT,token,NULL,256,public
From: https://blog.51cto.com/mose/7039055

相关文章

  • SolidWorks2022中文版图文安装教程、激活方法附安装包下载
    一、下载方式[软件名称]:SolidWorks2022[软件语言]:简体中文 [软件大小]:14.7G[安装环境]:Win11/Win10[硬件要求]:[email protected]内存@8G及以上下载链接%70%61%6E%2E%62%61%69%64%75%2E%63%6F%6D/%73/%31%4E%2D%4D%7A%53%41%34%71%32%76%70%5A%4F%51%4B%78%4E%4E%57%41%79%67?%70%77%64=%......
  • 格律诗乐器的生产全貌
    标题:探秘格律诗乐器的生产流程与质量控制在电视剧《天道》中,王志文饰演的角色展现了他对格律诗乐器的热爱和执着。这让我好奇格律诗乐器的生产流程和质量控制究竟是如何保证的。格律诗乐器的生产流程:一、原材料采购:格律诗乐器采用高品质的原材料来确保音色和外观的优秀表现。......
  • 天壤小白v1.1.0版本上线,超实用的大模型文档集功能优化Tips,快来一探究竟!
    Hi!小白的用户伙伴们,你们好!上月,「天壤小白」大模型产品三件套首次亮相2023WAIC。其中,应用开发平台最具特点的文档集功能,集成大模型强大的语义理解和推理技术,不仅有效破解大模型“幻觉”,还助力天壤快速适配企业需求,发布20+企业级智能应用。今天,天壤小白v1.1.0版本上线!除了「模型......
  • opencv-python图像金字塔
    图像金字塔是图像中多尺度表达的一种,主要用于图像特征检测,图像分割等领域,是一种以多分辨率来解释图像的有效但概念简单的结构。简单来说,图像金字塔是同一图像不同分辨率的子图集合。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐渐降低,且来源于同一张原始图的图像集合。层......
  • Hadoop初体验
    1、HDFS初体验既能够通过后台的终端命令进行文件的管理,也可以通过WebUI界面进行相关的操作;可以知道的是,WebUI界面模仿的是Linux系统的文件格式;2、示例说明可以使用hadoop文件夹里面自带的例子进行相关的测试......
  • 传承之韵:探秘《天道》中格律诗乐器的制作与质量控制
    传承之韵:探秘《天道》中格律诗乐器的制作与质量控制近年来,中国电视剧市场不断涌现出一系列优秀作品,其中以王志文主演的《天道》备受瞩目。该剧以其独特的题材和精湛的制作水平引发观众的广泛讨论。在剧中,格律诗乐器作为一个重要的元素,不仅丰富了剧情,更令人好奇这些古典音乐之美的......
  • 通过注册表关闭Windows 11的LE Audio功能 How to Disable LE Audio on Windows 11
    LEAudio是蓝牙音频的下一代技术,具有以下主要特点和优势:低能耗:LEAudio使用更高效的编解码器和低能量的蓝牙技术,从而降低了功耗,使设备的电池寿命更长。多设备连接:LEAudio支持多设备之间的同步连接,允许用户将多个蓝牙设备连接到同一音频源。广播音频:通过Auracast™广播音......
  • Springboot 测试@Test 工具
    别再用main方法测试了,太Low!这才是专业的SpringBoot项目测试方法!(qq.com)......
  • 一个轻量级的脚本规则引擎工具 Aviator Script
    告别ifelse!试试这款轻量级流程引擎吧,跟SpringBoot绝配!(qq.com) println("HelloAviatorScript!");a=b+c;println("a="+a);执行:packagecom.yuyi.advanced.pis;importcom.googlecode.aviator.AviatorEvaluator;importcom.googlecode.aviator.Expression......
  • java多线程:死锁
    一、死锁的定义   多线程以及多进程改善了系统资源的利用率并提高了系统的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。   所谓死锁是指两个或两个以上的线程在......