Spring Cloud微服務安全實戰_4-7_token&client信息持久化到數據庫


本篇講數據的持久化:

1,客戶端應用 持久化到數據庫

  之前的章節里,客戶端信息是在配置在代碼里的,是存在內存里的,這樣新增或刪除一個客戶端應用,都要改代碼,然后還要重啟認證服務器。

2,token 持久化到數據庫

  之前的章節里,token信息都是存在內存里的,這樣的話重啟服務器后,token就沒了。而且如果認證服務器是集群的話,發令牌的是A機器,驗令牌的可能是B機器,這樣也是不行的,需要將token持久化到數據庫或者redis。 

 

 Spring默認提供了OAuth2相關的表,建表語句如下( mysql ):

create table oauth_client_details (
  client_id VARCHAR(256) PRIMARY KEY,
  resource_ids VARCHAR(256),
  client_secret VARCHAR(256),
  scope VARCHAR(256),
  authorized_grant_types VARCHAR(256),
  web_server_redirect_uri VARCHAR(256),
  authorities VARCHAR(256),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(256)
);

create table oauth_client_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256)
);

create table oauth_access_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256),
  authentication BLOB,
  refresh_token VARCHAR(256)
);

create table oauth_refresh_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication BLOB
);

create table oauth_code (
  code VARCHAR(256), authentication BLOB
);

create table oauth_approvals (
    userId VARCHAR(256),
    clientId VARCHAR(256),
    scope VARCHAR(256),
    status VARCHAR(10),
    expiresAt DATETIME,
    lastModifiedAt DATETIME
);


CREATE TABLE SPRING_SESSION (
   PRIMARY_ID CHAR(36) NOT NULL,
   SESSION_ID CHAR(36) NOT NULL,
   CREATION_TIME BIGINT NOT NULL,
   LAST_ACCESS_TIME BIGINT NOT NULL,
   MAX_INACTIVE_INTERVAL INT NOT NULL,
   EXPIRY_TIME BIGINT NOT NULL,
   PRINCIPAL_NAME VARCHAR(100),
   CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
 );

 CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
 CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
 CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

 CREATE TABLE SPRING_SESSION_ATTRIBUTES (
  SESSION_PRIMARY_ID CHAR(36) NOT NULL,
  ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
  ATTRIBUTE_BYTES BLOB NOT NULL,
  CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
  CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
 );

 CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_PRIMARY_ID);

在mysql中 執行一下,目前用到的就是存token的表和存客戶端應用的表:

 

 

 下面開始改造代碼

1,由於要連數據,所以保證認證服務器配置了jdbc相關配置,我之前已配置過,用的是mybatis-plus,這里就不再贅述。

2,將客戶端信息保存到表里

之前是在代碼里配置:

 

 

 

將客戶端配置在數據庫里:

修改認證服務器代碼,刪掉客戶端的配置代碼,改為從數據庫里讀取

 

 

 3, 配置TokenStore,將token信息持久化

  TokenStoretoken 是一個接口,是用來存token的,默認的實現是內存的實現

 

 

 配置TokenStore

 

 告訴服務器,存取token的時候,去自定義的tokenStore里去存取token

 

 

 完整的配置類代碼:

package com.nb.security.server.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import javax.sql.DataSource;

/**
 * Created by: 李浩洋 on 2019-10-29
 *
 * 認證服務器
 **/
@Configuration  //這是一個配置類
@EnableAuthorizationServer //當前應用是一個認證服務器
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {//AuthorizationServerConfigurerAdapter:認證服務器適配器

    //Spring 對密碼加密的封裝,自己配置下
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;

    //tokenStore是進行存取token的接口,默認內存的實現還有redis,jdbc,jwt的實現(idea ctrl+H可看類關系)
    //這里配置用jdbc進行存取token
    @Bean
    public TokenStore tokenStore(){
        return new JdbcTokenStore(dataSource);
    }

    /**
     * 1,配置客戶端應用的信息,讓認證服務器知道有哪些客戶端應用來申請令牌。
     *
     * ClientDetailsServiceConfigurer:客戶端的詳情服務的配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /////////2--從數據庫里讀取客戶端應用配置信息,需要一個數據源,
        // spring會自動去  oauth_client_details 表里讀取客戶端信息
        clients.jdbc(dataSource);
    }


   /* @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        ///////////////1----配置在了內存里 //////////////////////
        clients.inMemory()//配置在內存里,后面修改為數據庫里
                //~============== 注冊【客戶端應用】,使客戶端應用能夠訪問認證服務器 ===========
                .withClient("orderApp")
                .secret(passwordEncoder.encode("123456")) //123456 加密后 $2a$10$smHIzJWYZGDUR7zvtDWDZuCw7awklq23MBll/vETtEHHd37gdl.9K
                .scopes("read","write") //orderApp有哪些權限
                .accessTokenValiditySeconds(3600) //token的有效期
                .resourceIds("order-server") //資源服務器的id。發給orderApp的token,能訪問哪些資源服務器,可以多個
                .authorizedGrantTypes("password")//授權方式,再給orderApp做授權的時候可以用哪種授權方式授權
                //~=============客戶端應用配置結束 =====================
                .and()
                //~============== 注冊【資源服務器-訂單服務】(因為訂單服務需要來認證服務器驗令牌),使訂單服務也能夠訪問認證服務器 ===========
                .withClient("orderService")
                .secret(passwordEncoder.encode("123456")) //123456 加密后 $2a$10$IW8NdL0L.0MPech5NmhYLen3vLj7tVeGrXi6sIV.u.WlBp1VKBcCm
                .scopes("read") //orderServer有哪些權限
                .accessTokenValiditySeconds(3600) //token的有效期
                .resourceIds("order-server") //資源服務器的id。
                .authorizedGrantTypes("password");//授權方式,
    }*/

    public static void main(String[] args) {
        //手動加密123456
        System.err.println(new BCryptPasswordEncoder().encode("123456"));
    }

    /**
     *,2,配置用戶信息
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //傳給他一個authenticationManager用來校驗傳過來的用戶信息是不是合法的,注進來一個,自己實現
        endpoints
                .tokenStore(tokenStore()) //告訴服務器要用自定義的tokenStore里去存取token
                .authenticationManager(authenticationManager);
    }


    /**
     * 3,配置資源服務器過來驗token 的規則
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        /**
         * 過來驗令牌有效性的請求,不是誰都能驗的,必須要是經過身份認證的。
         * 所謂身份認證就是,必須攜帶clientId,clientSecret,否則隨便一請求過來驗token是不驗的
         */
        security.checkTokenAccess("isAuthenticated()");
    }
}
View Code

 

重新請求令牌:

 

 

 

 可以看到,數據庫已經生成了token,

 

 重啟認證服務器,用這個token去調用資源服務器,也是可以成功的,說明token 已經存數據庫了

 

 

本章代碼github:https://github.com/lhy1234/springcloud-security/tree/chapt-4-7-tokendb

 

歡迎關注個人公眾號一起交流學習:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM