当前位置: 首页 > news >正文

spring security OAuth2 搭建资源服务器以及授权服务器/jdbc/jwt两种方案

一、认证服务器基于jdbc方式

如果不懂请移步上一篇文章:Spring security OAuth2 授权服务器搭建-CSDN博客

    在上一篇文章中,TokenStore的默认实现为 InHenoryTokenStore 即内存存储,对于 CLient 信息,userDetaitsServce 接负责从存储库中读取数据,在上面的案例中默认使用的也是 InHemoryCLientDetalsService 实现类。如果要想使用政据库存储,只要提供这些接口的实现类即可,而框架已经为我们写好 dbekenStore 和 dbcclientDetatsService

1.1 数据库所需表

我们使用spring security 自带的sql 语句生成对应的表结构,我们关注一下 oauth_client_details  这个表,这个表用来储存用户密钥信息,相当于前面的这个地方

clients.inMemory() //基于内存,后期可以入库查出来
        .withClient("client-lq")
        .secret(passwordEncoder.encode("secret-lq"))

-- 写入客户端信息
INSERT INTO oauth_client_details
VALUES ('client-id', NULL,'$2a$10$2McX6ml8CVK3RUNpLkX1zeQeNkrEvLCPOJ2hhpG18XMeIMbJWIJnK', 'read', 'authorization_code,refresh_token','http://www.baidu.com',NULL, NULL, NULL, NULL,NULL);

client_secret 需要加密存进去,否则会报不成功

--  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 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_clienttoken
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_nam          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;


select * from  oauth_refresh_token;

-- 写入客户端信息
INSERT INTO oauth_client_details
VALUES ('client-id', NULL,'$2a$10$2McX6ml8CVK3RUNpLkX1zeQeNkrEvLCPOJ2hhpG18XMeIMbJWIJnK', 'read', 'authorization_code,refresh_token','http://www.baidu.com',NULL, NULL, NULL, NULL,NULL);

1.2 认证服务器配置

@Configuration
@EnableAuthorizationServer  //指定当前应用为授权服务器
public class JdbcAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    @Autowired
    public JdbcAuthorizationServerConfig(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService, AuthenticationManager authenticationManager, DataSource dataSource) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
        this.authenticationManager = authenticationManager;
        this.dataSource = dataSource;
    }


    @Bean
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        // 使用哪一种加密方式
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    // 用来配置授权服务器可以为哪些客户端授权   client_id,secret  redirect_url
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());// 使用jdbc存储
    }


    @Bean
    public TokenStore tokenStore() {
        JdbcTokenStore jdbcTokenStore = new JdbcTokenStore(dataSource);
        return jdbcTokenStore;
    }


    // 配置令牌存储

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
       endpoints.tokenStore(tokenStore());// 配置令牌存储为数据库存储
        endpoints.userDetailsService(userDetailsService);

        // 配置 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天

        endpoints.tokenServices(tokenServices);// 使用配置令牌服务

    }

}

1.3 security 配置

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


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


    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        inMemoryUserDetailsManager.createUser(User.withUsername("root").password(passwordEncoder().encode("123456")).roles("ADMIN").build());
        return inMemoryUserDetailsManager;
    }


    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @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();
    }
}

1.4 数据库配置

@Configuration
@Slf4j
public class MysqlDsConfig {

    /**
     *  配置数据源
     * @return
     */
    @Primary
    @Bean
    public DataSource mysqlDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/oauth3");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("12345678");
        return dataSource;
    }

}

1.5 启动测试

当我们刷新token的时候 oauth_refresh_token 会有值进去,使用 apifox 测试令牌


二、资源服务器基于 jdbc 方式

需要连接数据库信息,跟认证服务器数据源一样,因为资源服务器最终也要去读取表信息

所需的依赖,资料参考 哔哩博主,不良人编程 

<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.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>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!-- 数据源支持 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </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>

2.1  资源服务器配置

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {


    private final DataSource dataSource;

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


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


    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }
}

2.2 测试controller

@RestController
public class HelloController {



    @RequestMapping("hello")
    public String hello() {
        System.out.println("hello resource server ");

        return "hello resource server";
    }
}

2.3 测试结果

当我们这个时候访问的时候就需要带上 Authorization:Bearer   参数,否则提示 Full authentication is required to access this resource 

curl -H "Authorization:Bearer token 对应的值" http://127.0.0.1:8083/hello


三、认证服务器基于 jwt 方式

jwt 分为三部分,用三个点隔开  ,第一部分标识 header 标识用那种加密方式;中间部分为subject 主体信息,可以是用户信息,也可以是一个json数据;最后一部分为sign 签名信息,这个信息需要设置足够复杂才能被破解;

3.1 认证服务器配置

可能在疑问,为什么还是要用数据库,这次我们只需要从库里面读取一次用户信息,只是使用一张表 oauth_client_details 其他信息都不会使用,jwt 有自动过期时间

@Configuration
@EnableAuthorizationServer  //指定当前应用为授权服务器
public class JwtAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    @Autowired
    public JwtAuthorizationServerConfig(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService, AuthenticationManager authenticationManager, DataSource dataSource) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
        this.authenticationManager = authenticationManager;
        this.dataSource = dataSource;
    }


    @Bean
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        // 使用哪一种加密方式
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    // 用来配置授权服务器可以为哪些客户端授权   client_id,secret  redirect_url
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());// 使用jdbc存储
    }



    // 使用同一个秘钥编码
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("12332111");
        return jwtAccessTokenConverter;
    }


    // jwt生成方式生成令牌
    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore jdbcTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jdbcTokenStore;
    }


    // 配置令牌存储

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
       endpoints.tokenStore(tokenStore())// 配置jwt令牌存储为数据库存储
         .accessTokenConverter(jwtAccessTokenConverter());
    }


    //http://127.0.0.1:8082/oauth/authorize?client_id=client&response_type=code&redirect_url=https://www.baidu.com

}

3.2 测试效果

1、页面授权效果有所不同,获取code

2、返回的令牌有所不同

api fox 效果

四、资源服务器基于 jwt 方式

    我们只需要跟认证服务器加密规则设置相同即可,因为jwt 自带加密算法,就类似于我们两个定义了一套rsa的秘钥,我们加密规则相同,约定一个签名戳,这个别人获取不到,你请求过来的时候我验证合法性,所以就不需要用到数据库了

4.1 资源服务器配置

@Configuration
@EnableResourceServer
public class JwtResourceServerConfig extends ResourceServerConfigurerAdapter {


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

    // jwt生成方式生成令牌
    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore jdbcTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jdbcTokenStore;
    }


    // 使用同一个秘钥编码
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("12332111");
        return jwtAccessTokenConverter;
    }
}

4.2 测试效果

curl -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjYzMTY1MTAsInVzZXJfbmFtZ
SI6InJvb3QiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjEyYTgyMmNhLWQ3MTgtNDE1Yy1hYWQ3LTA5ZjIxODNjNzY0YiIsImNsaWVudF9pZCI6ImNsaWVudC1scSIsInNjb3BlI
jpbInJlYWQ6dXNlciJdfQ.myzFE0VJOhlFLOsddBknOeB6Y499RwZ1X2zTM4PxC00" http://127.0.0.1:8083/hello
 

Z1X2zTM4PxC00" http://127.0.0.1:8083/hello
hello resource server
D:\java-tool\idea-project\spring-security-study>
 

使用apifox 测试

相关文章:

  • 飞速(FS)与西门子联合打造交换机自动化灌装测试生产线
  • Java多态
  • 【zabbix监控软件(配置及常用键值)】
  • 基于python+django+vue的个性化餐饮管理系统
  • Linux云计算 |【第三阶段】PROJECT1-DAY1
  • Java对象列表属性映射工具类
  • SQLite
  • HTML 转 PDF API 接口
  • SaaS化多租户实现的两种方法
  • MongoDB延迟查询
  • R语言统计分析——功效分析(比例、卡方检验)
  • Reactive 编程-Project Reactor
  • postcss 插件实现移动端适配
  • 力扣139-单词拆分(Java详细题解)
  • 基础Web开发的支持
  • .NET编程——利用C#调用海康机器人工业相机SDK实现回调取图与软触发取图【含免费源码】
  • 动态获取git版本号
  • FFmpega安装教程
  • 基于SpringBoot+Vue+MySQL的考编论坛网站
  • JVM: JDK内置命令 - JPS
  • 马上评|“为偶像正名”的正确做法是什么
  • 多地警务新媒体整合:关停交警等系统账号,统一信息发布渠道
  • 茅台1935今年动销达到预期,暂无赴港上市计划!茅台业绩会回应多个热点
  • 男子发寻母视频被警方批评教育,律师:发寻亲信息是正当行为
  • 城事 | 重庆新增热门打卡地标,首座熊猫主题轨交站亮相
  • 1至4月我国汽车产销量首次双超千万辆