展开
拓展 关闭
订阅号推广码
GitHub
视频
公告栏 关闭

权限认证(四):OAuth2认证服务器策略配置

  • 通过刷新令牌获取访问令牌
  • 新建CustomUserDetailsService
@Component("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
            return new User("admin", passwordEncoder.encode("1234"),
                    AuthorityUtils.commaSeparatedStringToAuthorityList("product"));
    }
}
  • SpringSecurityConfig中添加如下
    @Autowired
    private UserDetailsService customUserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService);
    }
  • AuthorizationServerConfig中添加如下
    @Autowired // 刷新令牌
    private UserDetailsService customUserDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 密码模式要设置认证管理器
        endpoints.authenticationManager(authenticationManager);
        // 刷新令牌时需要使用
        endpoints.userDetailsService(customUserDetailsService);
    }
  • postmam测试

  • 使用jdbc管理访问令牌

  • 新建数据库和表

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

-- ----------------------------
-- Records of clientdetails
-- ----------------------------

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

-- ----------------------------
-- Records of oauth_access_token
-- ----------------------------

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

-- ----------------------------
-- Records of oauth_approvals
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
  `client_id` varchar(128) NOT NULL COMMENT '客户端id(如:mengxuegu_client',
  `resource_ids` varchar(128) DEFAULT NULL,
  `client_secret` varchar(128) DEFAULT NULL COMMENT '客户端密码(要加密后存储)',
  `scope` varchar(128) DEFAULT NULL COMMENT '客户端授权范all,write,read)',
  `authorized_grant_types` varchar(128) DEFAULT NULL COMMENT '4种授权类型(多个授权类型,用英文逗号分隔',
  `web_server_redirect_uri` varchar(128) DEFAULT NULL COMMENT '获取授权码后的回调地址',
  `authorities` varchar(128) DEFAULT NULL COMMENT '授权标识',
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户端(第三方应用)基本信息';

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('mengxuegu-pc', 'product-server', '$2a$10$VcUHxmgnxBTdB9XNdhoOWujC.lFZ0rO1UizqMAS0GU6WAerFviX.a', 'all,PRODUCT_API', 'authorization_code,password,implicit,client_credentials,refresh_token', 'http://www.mengxuegu.com/', NULL, '50000', NULL, NULL, 'false');
INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('client1', '', '$2a$10$AodcXpujNILcZAQ.7cQ0b.Bm4klRumapoFBu7uyQ/ZV9Nu9F5fE3y', 'MEMBER_READ,MEMBER_WRITE', 'authorization_code,refresh_token', 'http://localhost:9001/login', NULL, '50000', NULL, NULL, 'true');
INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('client2', '', '$2a$10$AodcXpujNILcZAQ.7cQ0b.Bm4klRumapoFBu7uyQ/ZV9Nu9F5fE3y', 'MEMBER_READ', 'authorization_code,refresh_token', 'http://localhost:9002/login', NULL, '50000', NULL, NULL, 'true');


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

-- ----------------------------
-- Records of oauth_client_token
-- ----------------------------

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

-- ----------------------------
-- Records of oauth_code
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
  `token_id` varchar(128) DEFAULT NULL,
  `token` blob,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • pom.xml
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
  • yml
spring:
  # 数据源配置
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://192.168.1.102:3306/demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
    #mysql8版本以上驱动包指定新的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    #   数据源其他配置, 在 DruidConfig配置类中手动绑定
    initialSize: 8
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
  • 配置类
@Configuration
public class TokenConfig {
    // jdbc管理token
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource dataSource(){
        return new DruidDataSource();
    }
    @Bean
    public TokenStore tokenStore() {
        // jdbc管理令牌
        return new JdbcTokenStore(dataSource());
    }
}

# AuthorizationServerConfig类中添加如下
    @Autowired // token管理方式,在TokenConfig类中已对添加到容器中了
    private TokenStore tokenStore;
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 令牌的管理方式
        endpoints.tokenStore(tokenStore);
    }
  • 启动项目使用密码模式测试

  • 数据库中产生1条数据,测试通过

  • jdbc管理授权码

# AuthorizationServerConfig配置类中添加如下
    @Autowired 
    private DataSource dataSource;
    @Bean // 授权码管理策略
    public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 授权码管理策略 会产生的授权码放到 oauth_code 表中,如果这个授权码已经使用了,则对应这个表中的数据就会被删除
        endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
    }

# 之前的客户端信息储存在内存中,将其注释掉
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("mengxuegu-pc") // 客户端id
                .secret(passwordEncoder.encode("mengxuegu-secret")) // 客户端密码,需加密
                .resourceIds("product-server") // 资源id,可理解为微服务名称,表示可访问哪些微服务
                .authorizedGrantTypes("authorization_code", "password", "implicit", "client_credentials", "refresh_token")  // 认证模式
                .scopes("all") // 授权范围标识,哪部分资源可访问(all只是标识,不是说所有资源)
                .autoApprove(false) // false 跳到一个授权页面手动点击授权,true不需要手动点授权,直接响应一个授权码
                .redirectUris("http://www.mengxuegu.com/") // 客户端回调地址
                .accessTokenValiditySeconds(60*60*8) //访问令牌有效时长 默认为12小时
                .refreshTokenValiditySeconds(60*60*24*60) // 刷新令牌有效时长,默认是30天
            ;
    }
  • 未获取授权码前查看数据库
  • 获取授权码
# 浏览器访问http://localhost:8090/auth/oauth/authorize?client_id=mengxuegu-pc&response_type=code
# 输入用户名和密码:admin + 1234

  • 获取授权码后查看数据库

  • 使用授权码后,数据库自动删除授权码

  • 配置访问令牌和刷新令牌的有效时长

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 内存方式管理客户端信息
        clients.inMemory()
                .withClient("mengxuegu-pc") // 客户端id
                .secret(passwordEncoder.encode("mengxuegu-secret")) // 客户端密码,需加密
                .resourceIds("product-server") // 资源id,可理解为微服务名称,表示可访问哪些微服务
                .authorizedGrantTypes("authorization_code", "password", "implicit", "client_credentials", "refresh_token")  // 认证模式
                .scopes("all") // 授权范围标识,哪部分资源可访问(all只是标识,不是说所有资源)
                .autoApprove(false) // false 跳到一个授权页面手动点击授权,true不需要手动点授权,直接响应一个授权码
                .redirectUris("http://www.mengxuegu.com/") // 客户端回调地址
                .accessTokenValiditySeconds(60*60*8) //访问令牌有效时长 默认为12小时
                .refreshTokenValiditySeconds(60*60*24*60) // 刷新令牌有效时长,默认是30天
            ;
    }
  • oauth_client_details表字段详情
client_id :客户端ID
resource_ids :可访问的资源服务器ID, 不写则不校验.
client_secret :客户端密码,此处不能是明文,需要加密$2a$10$VcUHxmgnxBTdB9XNdhoOWujC.lFZ0rO1UizqMAS0GU6WAerFviX.a
scope :客户端授权范围, 不指定默认不校验范围
authorized_grant_types :客户端授权类型,支持多个使用逗号分隔authorization_code,password,implicit,client_credentials,refresh_token 
web_server_redirect_uri :服务器回调地址
autoapprove :false 显示授权点击页,true不显示自动授权
  • 使用jdbc管理第三方应用
# AuthorizationServerConfig类中添加如下
    @Autowired
    private DataSource dataSource;
    @Bean
    public ClientDetailsService jdbcClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }
    /**
     * 配置被允许访问此认证服务器的客户端信息
     * 1.内存方式
     * 2. 数据库方式
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // jdbc管理客户端
        clients.withClientDetails(jdbcClientDetailsService());
    }
  • 使用postman测试

  • 令牌端点详情

/oauth/authorize :申请授权码 code,  涉及的类 AuthorizationEndpoint 
/oauth/token :获取令牌 token,  涉及的类 TokenEndpoint 
/oauth/check_token :用于资源服务器请求端点来检查令牌是否有效,  涉及的类 CheckTokenEndpoint 
/oauth/confirm_access :用户确认授权提交,  涉及的类 WhitelabelApprovalEndpoint 
/oauth/error :授权服务错误信息,  涉及的类   WhitelabelErrorEndpoint 
/oauth/token_key :提供公有密匙的端点,使用 JWT 令牌时会使用 , 涉及的类 TokenKeyEndpoint
  • 开放端点
    /**
     * 令牌端点的安全配置
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 所有人可访问 /oauth/token_key 后面要获取公钥, 默认拒绝访问
        security.tokenKeyAccess("permitAll()");
        // 认证后可访问 /oauth/check_token , 默认拒绝访问
        security.checkTokenAccess("isAuthenticated()");
    }
  • 先获取一个令牌用于测试
  • 未开放时测试
  • 开放后测试
posted @ 2022-03-28 14:22  DogLeftover  阅读(166)  评论(0编辑  收藏  举报