本篇講數據的持久化:
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()"); } }
重新請求令牌:
可以看到,數據庫已經生成了token,
重啟認證服務器,用這個token去調用資源服務器,也是可以成功的,說明token 已經存數據庫了
本章代碼github:https://github.com/lhy1234/springcloud-security/tree/chapt-4-7-tokendb
歡迎關注個人公眾號一起交流學習: