Spring-Security-Oauth2 基于JDBC存储令牌和RBAC权限认证

Posted 何宝荣的午后红茶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring-Security-Oauth2 基于JDBC存储令牌和RBAC权限认证相关的知识,希望对你有一定的参考价值。

相关配置来自李哥博客:  https://funtl.com/zh/spring-security-oauth2/   (本文仅记录自己学习过程,说的不详细,可以观看李哥博客)

认证服务器和资源服务器pom.xml配置  (李哥博客用的是tk.mybatis 我用的是spring  jpa)

<dependencies>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</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>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

认证服务器: 

初始化JDBC相关表:

CREATE TABLE `clientdetails` (
  `appId` varchar(128) 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=utf8;

CREATE TABLE `oauth_access_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(128) 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=utf8;

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 NULL DEFAULT NULL,
  `lastModifiedAt` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(128) 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;

CREATE TABLE `oauth_client_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(128) NOT NULL,
  `user_name` varchar(256) DEFAULT NULL,
  `client_id` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `oauth_code` (
  `code` varchar(256) DEFAULT NULL,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `oauth_refresh_token` (
  `token_id` varchar(256) DEFAULT NULL,
  `token` blob,
  `authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 appllication.yml:

server:
  port: 9999
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: (自己的数据库)
    username: (账号)
    password: (密码)
    hikari:
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      pool-name: MyHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

 认证客服端:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        // 配置数据源(使用的是 HikariCP 连接池),以上注解是指定数据源,否则会有冲突
        return DataSourceBuilder.create().build();
    }
    @Bean
    public TokenStore tokenStore() {
        // 基于 JDBC 实现,令牌保存到数据
        return new JdbcTokenStore(dataSource());
    }

    @Bean
    public ClientDetailsService jdbcClientDetails() {
        // 基于 JDBC 实现,需要事先在数据库配置客户端信息
        return new JdbcClientDetailsService(dataSource());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 设置令牌
        endpoints.tokenStore(tokenStore());
    }
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 读取客户端配置
        clients.withClientDetails(jdbcClientDetails());
    }
}

用户授权:

@Configuration
public class UserDetailsServiceConfiguration implements UserDetailsService {
    @Autowired
    private TbUserService  tbUserService;
    @Autowired
    private TbPermissionService tbPermissionService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        TbUser tbUser  = tbUserService.findByUsername(username);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if (tbUser != null) {
            // 获取用户授权
            List<TbPermission> tbPermissions = tbPermissionService.selectByUserId(tbUser.getId());
            // 声明用户授权
            tbPermissions.forEach(tbPermission -> {
                if (tbPermission != null && tbPermission.getEnname() != null) {
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(tbPermission.getEnname());
                    grantedAuthorities.add(grantedAuthority);
                }
            });
        }

        return new User(tbUser.getUsername(), tbUser.getPassword(), grantedAuthorities);

    }
}

安全配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceConfiguration userDetailsServiceConfiguration;
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 设置默认的加密方式
        return new BCryptPasswordEncoder();
    }
    @Bean
    @Override
    public UserDetailsService userDetailsService(){
        return userDetailsServiceConfiguration;
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自定义认证与授权
        auth.userDetailsService(userDetailsService());
    }
    @Override
    public void configure(WebSecurity web)  {
        // 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
        web.ignoring().antMatchers("/oauth/check_token");
    }
}

 获取用户信息和权限信息目录和代码:

@Repository
public  interface TbPermissionRepository extends JpaRepository<TbPermission,Long> {
    @Query(value = "SELECT\\n" +
            "  p.*\\n" +
            "FROM\\n" +
            "  tb_user AS u\\n" +
            "  LEFT JOIN tb_user_role AS ur\\n" +
            "    ON u.id = ur.user_id\\n" +
            "  LEFT JOIN tb_role AS r\\n" +
            "    ON r.id = ur.role_id\\n" +
            "  LEFT JOIN tb_role_permission AS rp\\n" +
            "    ON r.id = rp.role_id\\n" +
            "  LEFT JOIN tb_permission AS p\\n" +
            "    ON p.id = rp.permission_id\\n" +
            "WHERE u.id = ?1",nativeQuery = true)
    public List<TbPermission> selectByUserId(Long id);
}
@Repository
public interface TbUserRepository extends JpaRepository<TbUser,Long> {
    @Query(value = "select * from tb_user where username = ?1",nativeQuery = true)
    public TbUser findByUsername(String username);
}
@Service
public class TbPermissionService{
    @Autowired
    private TbPermissionRepository tbPermissionRepository;
    public List<TbPermission> selectByUserId(Long id){
        List<TbPermission> tbPermissions = tbPermissionRepository.selectByUserId(id);
        return tbPermissions;
    }
}
@Service
public class TbUserService {
    @Autowired
    private TbUserRepository tbUserRepository;
    public TbUser findByUsername(String username){
        TbUser byUsername = tbUserRepository.findByUsername(username);
        return byUsername;
    }
}

客服端表:oauth_client_details

获取code请求地址(GET请求):  http://{you ip}:{you port}/oauth/authorize?client_id=client&response_type=code (client_id为上图中的client_id,成功之后会回调url 为上面的web_server_redicrct_uri)

获取token请求地址(POST请求:) :http://client:secret@{you ip}:{you port}/oauth/token   携带参数为: grant_type (为上图中的authorization_grant_type)  code (上面返回的code)

资源服务器:  资源服务器就是普通的CRUD和对接Oauth2认证授权服务端

RBAC相关表:

 

CREATE TABLE `tb_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `parent_id` bigint(20) DEFAULT NULL COMMENT \'父权限\',
  `name` varchar(64) NOT NULL COMMENT \'权限名称\',
  `enname` varchar(64) NOT NULL COMMENT \'权限英文名称\',
  `url` varchar(255) NOT NULL COMMENT \'授权路径\',
  `description` varchar(200) DEFAULT NULL COMMENT \'备注\',
  `created` datetime NOT NULL,
  `updated` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8 COMMENT=\'权限表\';
insert  into `tb_permission`(`id`,`parent_id`,`name`,`enname`,`url`,`description`,`created`,`updated`) values 
(37,0,\'系统管理\',\'System\',\'/\',NULL,\'2019-04-04 23:22:54\',\'2019-04-04 23:22:56\'),
(38,37,\'用户管理\',\'SystemUser\',\'/users/\',NULL,\'2019-04-04 23:25:31\',\'2019-04-04 23:25:33\'),
(39,38,\'查看用户\',\'SystemUserView\',\'\',NULL,\'2019-04-04 15:30:30\',\'2019-04-04 15:30:43\'),
(40,38,\'新增用户\',\'SystemUserInsert\',\'\',NULL,\'2019-04-04 15:30:31\',\'2019-04-04 15:30:44\'),
(41,38,\'编辑用户\',\'SystemUserUpdate\',\'\',NULL,\'2019-04-04 15:30:32\',\'2019-04-04 15:30:45\'),
(42,38,\'删除用户\',\'SystemUserDelete\',\'\',NULL,\'2019-04-04 15:30:48\',\'2019-04-04 15:30:45\');

CREATE TABLE `tb_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `parent_id` bigint(20) DEFAULT NULL COMMENT \'父角色\',
  `name` varchar(64) NOT NULL COMMENT \'角色名称\',
  `enname` varchar(64) NOT NULL COMMENT \'角色英文名称\',
  `description` varchar(200) DEFAULT NULL COMMENT \'备注\',
  `created` datetime NOT NULL,
  `updated` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT=\'角色表\';
insert  into `tb_role`(`id`,`parent_id`,`name`,`enname`,`description`,`created`,`updated`) values 
(37,0,\'超级管理员\',\'admin\',NULL,\'2019-04-04 23:22:03\',\'2019-04-04 23:22:05\');

CREATE TABLE `tb_role_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_id` bigint(20) NOT NULL COMMENT \'角色 ID\',
  `permission_id` bigint(20) NOT NULL COMMENT \'权限 ID\',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COMMENT=\'角色权限表\';
insert  into `tb_role_permission`(`id`,`role_id`,`permission_id`) values 
(37,37,37),
(38,37,38),
(39,37,39),
(40,37,40),
(41,37,41),
(42,37,42);

CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT \'用户名\',
  `password` varchar(64) NOT NULL COMMENT \'密码,加密存储\',
  `phone` varchar(20) DEFAULT NULL COMMENT \'注册手机号\',
  `email` varchar(50) DEFAULT NULL COMMENT \'注册邮箱\',
  `created` datetime NOT NULL,
  `updated` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`) USING BTREE,
  UNIQUE KEY `phone` (`phone`) USING BTREE,
  UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT=\'用户表\';
insert  into `tb_user`(`id`,`username`,`password`,`phone`,`email`,`created`,`updated`) values 
(37,\'admin\',\'$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi\',\'15888888888\',\'lee.lusifer@gmail.com\',\'2019-04-04 23:21:27\',\'2019-04-04 23:21:29\');

CREATE TABLE `tb_user_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL COMMENT \'用户 ID\',
  `role_id` bigint(20) NOT NULL COMMENT \'角色 ID\',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT=\'用户角色表\';
insert  into `tb_user_role`(`id`,`user_id`,`role_id`) values 
(37,37,37);

application.yml:

server:
  port: 10000
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: (自己的数据库)
    username: (账号)
    password: (密码)
    hikari:
      minimum-idle: 5
      idle-timeout: 600000
      maximum-pool-size: 10
      auto-commit: true
      pool-name: MyHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1
security:
  oauth2:
    client:
      client-id: client
      client-secret: secret
      access-token-uri: http://localhost:9999/oauth/token
      user-authorization-uri: http://localhost:9999/oauth/authorize
    resource:
      token-info-uri: http://localhost:9999/oauth/check_token
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 以下为配置所需保护的资源路径及权限,需要与认证服务器配置的授权部分对应
                .antMatchers("/").hasAuthority("System")
                .antMatchers("/view/**").hasAuthority("SystemContentView")
                .antMatchers("/insert/**").hasAuthority("SystemContentInsert")
                .antMatchers("/update/**").hasAuthority("SystemContentUpdate")
                .antMatchers("/delete/**").hasAuthority("SystemContentDelete");
    }
}

以上是关于Spring-Security-Oauth2 基于JDBC存储令牌和RBAC权限认证的主要内容,如果未能解决你的问题,请参考以下文章

spring-security-oauth2中的HttpSecurity配置问题

如何处理 spring-security-oauth2 的版本升级?

spring-security-oauth2 替代品

Spring-Security-Oauth2:默认登录成功 url

spring-security-oauth2 2.0.7 刷新令牌 UserDetailsS​​ervice 配置 - UserDetailsS​​ervice 是必需的

spring-security-oauth2注解详解