oauth2.0 JdbcClientDetailsServiceを使用してデータベースから適切な構成を読み込む
17043 ワード
前節では、ライセンスコードをredisに格納し、対応するリクエストのパスをin-memoryで格納する構成について説明しましたが、これはメモリに格納されていますが、実際に開発された私たちのデータはデータテーブルからクエリーされることを望んでいますが、どうすればいいのでしょうか.
1.in-memoryストレージのレビュー
この方法を使用している場合、対応する認証コードの要求経路は以下の通りです.
該当するパラメータを照合してください
次にjwtのトークン方式を使用し、対応するリクエストパスは以下の通りです.
これはメモリに格納するストレージ方式です
2.データベースから該当するフィールドのパラメータを読み込む必要がある場合は、次のように構成します.
ここでは、以前のメモリからの読み取り方法を削除し、代わりにclientDetails()という方法を見て、この方法を見てみましょう.
このbeanを構成するだけでいいですが、私たちのdatasourceはymlプロファイルで構成されています.注入するだけです.
しかし、ここではまだ終わっていません.まず、JdbcClientDetailsServiceがデータベースからどのように読み込まれたかについてお話しします.次のように、アクセスして対応するソースコードを表示できます.
彼自身がデフォルトのフィールドを持つテーブルであり、対応するクエリーの方法があるため、sqlは以下のようなテーブルを作成する必要があります.
これはデフォルトのクラスのテーブルです.一般的にはデフォルトでいいです.ここでは、上記のフィールドに基づいて関連する内容を構成する必要があります.以下のようにします.
ここで設定したアクセスパスは、次のとおりです.
トークンのアクセスパスは次のとおりです.
codeを前のステップで生成したcodeに置き換えるのを覚えています
3.落とし穴の回顧:
以前はトークンtokenを格納するためにjwtを使用していましたが、jwtokenはどうしても現れないことに気づきました.何度もチェックした結果、エラーが見つかりました.コードは以下の通りです.
注意:DefaultTokenServicesとjwttokenの構成はすべて存在しないでください.そうしないと、システムはDefaultTokenServicesの構成のみを探します.つまり、生成されたtokenは常にデフォルトのUUIDになります.ここでは、どちらかの構成をコードに選択するしかありません.
4.コツ:
パス認可後にcodeを取得すると、jwttokenをcodeで要求し、postmanでテストすることができます.
ここではbasic Authでname:normal-appを記入するだけでいいので、パスワードは記入しなくてもいいです.
得られたjsonを見てみましょう
ここで生成するjwtokenにはこれに対応するjwtの情報が携帯されているが、この文字列は実際にはHeaderとPayloadが暗号化してつなぎ合わせたものであり、それに応じて次のjwtの関連解析を見ることができる.
以下にアクセスできます.https://www.jsonwebtoken.io/このtokenの情報を解析します.
ここではHeaderとPayloadの情報を見ることができます.Headerは主にtypeと暗号化アルゴリズムを格納しています.ここではHS 256です.私たちは主にPayloadの情報を見ています.
ここではログインするユーザ名、tokenのライフサイクルなどを見ることができる.生成されたjwttokenが携帯する情報がどのようなものかをより明確に理解することができます.
1.in-memoryストレージのレビュー
/**
* inMemory
*/
clients.inMemory()
//client Id
.withClient("normal-app")
.authorizedGrantTypes("authorization_code", "implicit")
.authorities("ROLE_CLIENT")
.scopes("read","write")
.resourceIds(resourceId)
.accessTokenValiditySeconds(accessTokenValiditySeconds)//
.and()
.withClient("trusted-app")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.resourceIds(resourceId)
.accessTokenValiditySeconds(accessTokenValiditySeconds)
.secret("secret");
この方法を使用している場合、対応する認証コードの要求経路は以下の通りです.
http://localhost:8787/oauth/authorize?client_id=normal-app&response_type=code&scope=read&redirect_uri=/resources/user
該当するパラメータを照合してください
次にjwtのトークン方式を使用し、対応するリクエストパスは以下の通りです.
http://localhost:8787/oauth/token?code=r8YBUL&grant_type=authorization_code&client_id=normal-app&redirect_uri=/resources/user
これはメモリに格納するストレージ方式です
2.データベースから該当するフィールドのパラメータを読み込む必要がある場合は、次のように構成します.
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// InMemoryTokenStore ( , , )。 , , 。
// JdbcTokenStore JDBC , 。 , JDBC , , , , 。 JdbcTokenStore “spring-jdbc” 。
// jdbc
clients.withClientDetails(clientDetails());
}
ここでは、以前のメモリからの読み取り方法を削除し、代わりにclientDetails()という方法を見て、この方法を見てみましょう.
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
このbeanを構成するだけでいいですが、私たちのdatasourceはymlプロファイルで構成されています.注入するだけです.
import javax.sql.DataSource;
@Resource
private DataSource dataSource;
しかし、ここではまだ終わっていません.まず、JdbcClientDetailsServiceがデータベースからどのように読み込まれたかについてお話しします.次のように、アクセスして対応するソースコードを表示できます.
public JdbcClientDetailsService(DataSource dataSource) {
this.updateClientDetailsSql = DEFAULT_UPDATE_STATEMENT;
this.updateClientSecretSql = "update oauth_client_details set client_secret = ? where client_id = ?";
this.insertClientDetailsSql = "insert into oauth_client_details (client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, client_id) values (?,?,?,?,?,?,?,?,?,?,?)";
this.selectClientDetailsSql = "select client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from oauth_client_details where client_id = ?";
this.passwordEncoder = NoOpPasswordEncoder.getInstance();
Assert.notNull(dataSource, "DataSource required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(this.jdbcTemplate));
}
彼自身がデフォルトのフィールドを持つテーブルであり、対応するクエリーの方法があるため、sqlは以下のようなテーブルを作成する必要があります.
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(48) 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;
これはデフォルトのクラスのテーブルです.一般的にはデフォルトでいいです.ここでは、上記のフィールドに基づいて関連する内容を構成する必要があります.以下のようにします.
ここで設定したアクセスパスは、次のとおりです.
// :
http://localhost:8787/oauth/token?client_id=normal-app&grant_type=authorization_code&code=1oCj8e&redirect_uri=http://localhost:8787/resources/user
トークンのアクセスパスは次のとおりです.
// :
http://localhost:8787/resources/user?access_token=9d62c7b0-780e-4c6a-ad5a-56d79a089342
codeを前のステップで生成したcodeに置き換えるのを覚えています
3.落とし穴の回顧:
以前はトークンtokenを格納するためにjwtを使用していましたが、jwtokenはどうしても現れないことに気づきました.何度もチェックした結果、エラーが見つかりました.コードは以下の通りです.
package urity.demo.oauth2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import urity.demo.service.RedisAuthenticationCodeServices;
import urity.demo.support.MyUserDetailService;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Resource
private DataSource dataSource;
@Value("${resource.id:spring-boot-application}")// spring-boot-application
private String resourceId;
@Value("${access_token.validity_period:36000}")
private int accessTokenValiditySeconds = 36000;
// security
@Resource
private AuthenticationManager authenticationManager;
@Resource
private RedisAuthenticationCodeServices redisAuthenticationCodeServices;
@Resource
private MyUserDetailService myUserDetailService;
//security
// 。
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
// Bean
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
// @Bean // TokenStore
// public JdbcTokenStore jdbcTokenStore() {
// return new JdbcTokenStore(dataSource);
// }
// ClientDetailsServiceConfigurer( AuthorizationServerConfigurer) JDBC 。
//clientId:( ) ID。
//secret:( ) ( )。
//scope: 。 ( ), 。read write all
//authorizedGrantTypes: 。 。
//authorities ( Spring Security )。
// ( , JdbcClientDetailsService) ClientDetailsManager ( ClientDetailsService ) 。
// :JDBC ( )
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// InMemoryTokenStore ( , , )。 , , 。
// JdbcTokenStore JDBC , 。 , JDBC , , , , 。 JdbcTokenStore “spring-jdbc” 。
// /**
// * inMemory
// */
// clients.inMemory()
// //client Id
// .withClient("normal-app")
// .authorizedGrantTypes("authorization_code", "implicit")
// .authorities("ROLE_CLIENT")
// .scopes("read","write")
// .resourceIds(resourceId)
// .accessTokenValiditySeconds(accessTokenValiditySeconds)//
// .and()
// .withClient("trusted-app")
// .authorizedGrantTypes("client_credentials", "password")
// .authorities("ROLE_TRUSTED_CLIENT")
// .scopes("read", "write")
// .resourceIds(resourceId)
// .accessTokenValiditySeconds(accessTokenValiditySeconds)
// .secret("secret");
// jdbc
clients.withClientDetails(clientDetails());
}
//AuthorizationEndpoint AuthorizationServerEndpointsConfigurer。 , , ( , )。 :
//authenticationManager: AuthenticationManager。
//userDetailsService: UserDetailsService ( a GlobalAuthenticationManagerConfigurer), ,
//authorizationCodeServices: AuthorizationCodeServices ( )。
//implicitGrantService: 。
//tokenGranter:(TokenGranter )
// XML authorization-server。
/**
* /oauth/authorize ,
* ,
* 。
* UserApprovalHandler,
* AuthorizationEndpoint
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
endpoints.accessTokenConverter(accessTokenConverter());//jwt
//
// endpoints.tokenStore(jdbcTokenStore());
// jwt
endpoints.tokenStore(jwtStore());
//
endpoints.authorizationCodeServices(redisAuthenticationCodeServices);
endpoints.userDetailsService(myUserDetailService);
// TokenServices uuid jwt jwt
// DefaultTokenServices tokenServices = new DefaultTokenServices();
// jdbc
// tokenServices.setTokenStore(endpoints.getTokenStore());
// jwt token
// tokenServices.setTokenStore(jwtStore());
// tokenServices.setSupportRefreshToken(false);
// tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
// tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
// tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30
// endpoints.tokenServices(tokenServices);
// @Bean
// RedisTokenStore redisTokenStore(){
// return new RedisTokenStore(redisConnectionFactory);
// }
// endpoints.tokenStore(redisTokenStore());
}
// jwttoken
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
/**
* token
*
*
*/
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication().getName();
// UserDetail link{SecurityConfiguration}
User user = (User) authentication.getUserAuthentication().getPrincipal();
/** token ***/
final Map additionalInformation = new HashMap<>();
additionalInformation.put("userName", userName);
additionalInformation.put("roles", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
return enhancedToken;
}
};
// , , RSA
accessTokenConverter.setSigningKey("123");
return accessTokenConverter;
}
@Bean
public TokenStore jwtStore() {
TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
return tokenStore;
}
/**
* token
*
* @return
*/
@Bean
public ResourceServerTokenServices defaultTokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
defaultTokenServices.setTokenStore(jwtStore());
return defaultTokenServices;
}
}
注意:DefaultTokenServicesとjwttokenの構成はすべて存在しないでください.そうしないと、システムはDefaultTokenServicesの構成のみを探します.つまり、生成されたtokenは常にデフォルトのUUIDになります.ここでは、どちらかの構成をコードに選択するしかありません.
/ TokenServices uuid jwt jwt
// DefaultTokenServices tokenServices = new DefaultTokenServices();
// jdbc
// tokenServices.setTokenStore(endpoints.getTokenStore());
// jwt token
// tokenServices.setTokenStore(jwtStore());
// tokenServices.setSupportRefreshToken(false);
// tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
// tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
// tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30
// endpoints.tokenServices(tokenServices);
4.コツ:
パス認可後にcodeを取得すると、jwttokenをcodeで要求し、postmanでテストすることができます.
ここではbasic Authでname:normal-appを記入するだけでいいので、パスワードは記入しなくてもいいです.
http://localhost:8787/oauth/token?client_id=normal-app&grant_type=authorization_code&code=csTjhK&redirect_uri=http://localhost:8787/resources/user
得られたjsonを見てみましょう
ここで生成するjwtokenにはこれに対応するjwtの情報が携帯されているが、この文字列は実際にはHeaderとPayloadが暗号化してつなぎ合わせたものであり、それに応じて次のjwtの関連解析を見ることができる.
以下にアクセスできます.https://www.jsonwebtoken.io/このtokenの情報を解析します.
ここではHeaderとPayloadの情報を見ることができます.Headerは主にtypeと暗号化アルゴリズムを格納しています.ここではHS 256です.私たちは主にPayloadの情報を見ています.
{
"aud": [
"resourceId"
],
"user_name": "test",
"scope": [
"read"
],
"roles": [
{
"authority": "ROLE_USER"
},
{
"authority": "admin"
}
],
"exp": 1532662701,
"userName": "test",
"authorities": [
"admin",
"ROLE_USER"
],
"jti": "066cefa0-0a7a-40da-87a0-133c5a9c64d3",
"client_id": "normal-app",
"iat": 1532659101
}
ここではログインするユーザ名、tokenのライフサイクルなどを見ることができる.生成されたjwttokenが携帯する情報がどのようなものかをより明確に理解することができます.