美文网首页编程杂货铺SpringbootJavaWeb
Spring Cloud OAuth2 token存数据库实现

Spring Cloud OAuth2 token存数据库实现

作者: 会动的木头疙瘩儿 | 来源:发表于2018-08-30 17:19 被阅读1次

    本文是基于上一篇 Spring Cloud OAuth2教程 实现的token入库,并且用户从数据库中读取;
    源码地址:https://github.com/liangxiaobo/test-security-oauth2/tree/master-jdbc 注意是分支master-jdbc

    1 改造service-auth项目

    1.1 添加pom依赖

           <dependency>
              <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    

    1.2 application.yml添加数据库配置

    spring:
      application:
        name: service-auth
    
      datasource:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://172.16.10.44:3306/spring-cloud-auth2-db?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
          username: dev
          password: NHdev2015
    
      jpa:
        hibernate:
          ddl-auto: update
        show-sql: true
    
      后面的省略......
    

    1.3 修改 AuthorizationServerConfiguration类

    
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        AuthenticationManager authenticationManager;
    
        @Autowired
        RedisConnectionFactory redisConnectionFactory;
    
        @Autowired
        private DataSource dataSource;
    
        @Autowired
        private TokenStore tokenStore;
    
        @Autowired
        private UserServiceDetail userServiceDetail;
    
        @Autowired
        private ClientDetailsService clientDetailsService;
    
        static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
    
        @Bean
        public TokenStore tokenStore() {
            return new JdbcTokenStore(dataSource);
        }
    
        @Bean // 声明 ClientDetails实现
        public ClientDetailsService clientDetailsService() {
            return new JdbcClientDetailsService(dataSource);
        }
    
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.withClientDetails(clientDetailsService);
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            // redisTokenStore
    //        endpoints.tokenStore(new MyRedisTokenStore(redisConnectionFactory))
    //                .authenticationManager(authenticationManager)
    //                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    
            // 存数据库
            endpoints.tokenStore(tokenStore).authenticationManager(authenticationManager)
                    .userDetailsService(userServiceDetail);
    
            // 配置tokenServices参数
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setTokenStore(endpoints.getTokenStore());
            tokenServices.setSupportRefreshToken(false);
            tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
            tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
            tokenServices.setAccessTokenValiditySeconds((int)TimeUnit.DAYS.toSeconds(30)); // 30天
            endpoints.tokenServices(tokenServices);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            // 允许表单认证
            security.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()")
                    .checkTokenAccess("isAuthenticated()");
        }
    }
    
    

    其中 增加的Bean分别有dataSource, tokenStore, clientDetailsService, userServiceDetail,修改了ClientDetailsServiceConfigurer,AuthorizationServerEndpointsConfigurer

    1.3.1 tokenStore

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

    1.3.2 clientDetailsService

    @Bean // 声明 ClientDetails实现
        public ClientDetailsService clientDetailsService() {
            return new JdbcClientDetailsService(dataSource);
        }
    

    1.3.3 userServiceDetail

    userServiceDetail是org.springframework.security.core.userdetails.UserDetailsService的自定义实现

    @Service
    public class UserServiceDetail  implements UserDetailsService {
    
        @Autowired
        private UserDao userRepository;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            return userRepository.findByUsername(username);
        }
    }
    

    UserDao 继承了JpaRepository

    public interface UserDao extends JpaRepository<User, Long> {
        User findByUsername(String username);
    }
    

    User类

    @Entity
    public class User implements UserDetails, Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(nullable = false, unique = true)
        private String username;
    
        @Column
        private String password;
    
        @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"))
        private List<Role> authorities;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public void setAuthorities(List<Role> authorities) {
            this.authorities = authorities;
        }
    
    
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return password;
        }
    
        @Override
        public String getUsername() {
            return username;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    
    

    Role类

    @Entity
    public class Role implements GrantedAuthority {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(nullable = false)
        private String name;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String getAuthority() {
            return name;
        }
    
        @Override
        public String toString() {
            return "Role{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    

    1.4 改造 SecurityConfiguration 类

    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Bean
        PasswordEncoder passwordEncoder() {
            return PasswordEncoderFactories.createDelegatingPasswordEncoder();
        }
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            AuthenticationManager manager = super.authenticationManagerBean();
            return manager;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.requestMatchers().anyRequest()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/oauth/**").permitAll();
        }
    }
    

    到此service-auth项目改造完成,这里有一个官方提供的oauth2相关的数据库脚本
    https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql 官方给的不能直接创建,需要修改,把主键长度256改为128,把LONGVARBINARY类型改为BLOB,下面是我改好的:

    schema.sql

    -- used in tests that use HSQL
    create table oauth_client_details (
      client_id VARCHAR(128) PRIMARY KEY,
      resource_ids VARCHAR(128),
      client_secret VARCHAR(128),
      scope VARCHAR(128),
      authorized_grant_types VARCHAR(128),
      web_server_redirect_uri VARCHAR(128),
      authorities VARCHAR(128),
      access_token_validity INTEGER,
      refresh_token_validity INTEGER,
      additional_information VARCHAR(4096),
      autoapprove VARCHAR(128)
    );
    
    create table oauth_client_token (
      token_id VARCHAR(128),
      token BLOB,
      authentication_id VARCHAR(128) PRIMARY KEY,
      user_name VARCHAR(128),
      client_id VARCHAR(128)
    );
    
    create table oauth_access_token (
      token_id VARCHAR(128),
      token BLOB,
      authentication_id VARCHAR(128) PRIMARY KEY,
      user_name VARCHAR(128),
      client_id VARCHAR(128),
      authentication BLOB,
      refresh_token VARCHAR(128)
    );
    
    create table oauth_refresh_token (
      token_id VARCHAR(128),
      token BLOB,
      authentication BLOB
    );
    
    create table oauth_code (
      code VARCHAR(128), authentication BLOB
    );
    
    create table oauth_approvals (
        userId VARCHAR(128),
        clientId VARCHAR(128),
        scope VARCHAR(128),
        status VARCHAR(10),
        expiresAt TIMESTAMP,
        lastModifiedAt TIMESTAMP
    );
    
    
    -- customized oauth_client_details table
    create table ClientDetails (
      appId VARCHAR(128) PRIMARY KEY,
      resourceIds VARCHAR(128),
      appSecret VARCHAR(128),
      scope VARCHAR(128),
      grantTypes VARCHAR(128),
      redirectUrl VARCHAR(128),
      authorities VARCHAR(128),
      access_token_validity INTEGER,
      refresh_token_validity INTEGER,
      additionalInformation VARCHAR(4096),
      autoApproveScopes VARCHAR(128)
    );
    
    

    看数据库的数据存储 表oauth_client_details


    k1.png

    还缺少用户信息,用户会在service-hi中创建;

    2 改造service-hi项目

    这个项目改造的比较简单就是如果创建一个用户到数据库

    2.1 pom添加依赖

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    

    2.2 application.yml 在原有的基础上添加

      datasource:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://172.16.10.44:3306/spring-cloud-auth2-db?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
          username: 用户名
          password: 密码
    
      jpa:
        hibernate:
          ddl-auto: update
        show-sql: true
    

    2.3 在TestEndPointController中添加一个接口 registry

    @RequestMapping(value = "/registry", method = RequestMethod.POST)
        public User createUser(@RequestParam("username") String username, @RequestParam("password") String password) {
            if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) {
                return userService.create(username, password);
            }
    
            return null;
        }
    

    Bean userService是数据库访问层的实现;

    2.4 定义接口 UserService

    public interface UserService {
        public User create(String username, String password);
    }
    

    2.5 UserService的实现UserServiceImpl

    @Service
    public class UserServiceImpl implements UserService {
    
        private static final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    
        @Autowired
        private UserDao userDao;
    
        @Override
        public User create(String username, String password) {
            User user = new User();
            user.setUsername(username);
            password = "{bcrypt}" + passwordEncoder.encode(password);
            user.setPassword(password);
            User u = userDao.save(user);
            return u;
        }
    }
    

    2.6 User,Role

    和上面的User,Role一样

    配置都以完成,可以测试了,依次启动eureka-server、service-auth、service-hi
    访问 http://localhost:8765/registry 提交表单

    k2.png
    现在数据库的user表中多了一个用户,然后拿用户去请求token
    访问 http://localhost:9098/oauth/token
    k3.png

    注意到密码为什么是{bcrypt}$2a$10$7qkIuNV0gLeCX8XVILhC3e0kVSNH0.kfLqYlk79vwwozb8YMAkhLi,我找了一个别人写的博客 https://www.cnkirito.moe/spring-security-6/
    官方文档 https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#core-services-password-encoding

    k5.png

    喜欢的朋友请赞赏支持一下

    相关文章

      网友评论

      • 8c374ac87f6f:{
        "error": "invalid_client",
        "error_description": "Bad client credentials"
        }
        我这边在跑获取token的接口的时候,一直报这个错,疯了!
        会动的木头疙瘩儿:@浮城_e1f2 一起学习研究
        浮城_e1f2:@会动的木头疙瘩儿 在下学好几天了,快崩溃了。终于遇到最新的版本了:sob:
        会动的木头疙瘩儿:@李敬_998e 检查一下你的密码格式 ,是不是符合规则的,之前我也在这上面被坑过!

      本文标题:Spring Cloud OAuth2 token存数据库实现

      本文链接:https://www.haomeiwen.com/subject/jssdwftx.html