Spring Security OAuth2實(shí)現(xiàn)使用JWT的示例代碼
1、概括
在博客中,我們將討論如何讓Spring Security OAuth2實(shí)現(xiàn)使用JSON Web Tokens。
2、Maven 配置
首先,我們需要在我們的pom.xml中添加spring-security-jwt依賴項(xiàng)。
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency>
我們需要為Authorization Server和Resource Server添加spring-security-jwt依賴項(xiàng)。
3、授權(quán)服務(wù)器
接下來,我們將配置我們的授權(quán)服務(wù)器使用JwtTokenStore - 如下所示
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
請注意,在JwtAccessTokenConverter中使用了一個(gè)對稱密鑰來簽署我們的令牌 - 這意味著我們需要為資源服務(wù)器使用同樣的確切密鑰。
4、資源服務(wù)器
現(xiàn)在,我們來看看我們的資源服務(wù)器配置 - 這與授權(quán)服務(wù)器的配置非常相似:
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer config) {
config.tokenServices(tokenServices());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
}
請記住,我們將這兩個(gè)服務(wù)器定義為完全獨(dú)立且可獨(dú)立部署的服務(wù)器。這就是我們需要在新配置中再次聲明一些相同的bean的原因。
5、令牌中的自定義聲明
現(xiàn)在讓我們設(shè)置一些基礎(chǔ)設(shè)施,以便能夠在訪問令牌中添加一些自定義聲明。框架提供的標(biāo)準(zhǔn)聲明都很好,但大多數(shù)情況下我們需要在令牌中使用一些額外的信息來在客戶端使用。 我們將定義一個(gè)TokenEnhancer來定制我們的Access Token與這些額外的聲明。 在下面的例子中,我們將添加一個(gè)額外的字段“組織”到我們的訪問令牌 - 與此CustomTokenEnhancer:
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(
OAuth2AccessToken accessToken,
OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("organization", authentication.getName() + randomAlphabetic(4));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
然后,我們將把它連接到我們的授權(quán)服務(wù)器配置 - 如下所示:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
有了這個(gè)新的配置啟動(dòng)和運(yùn)行 - 這是一個(gè)令牌令牌有效載荷看起來像:
{
"user_name": "john",
"scope": [
"foo",
"read",
"write"
],
"organization": "johnIiCh",
"exp": 1458126622,
"authorities": [
"ROLE_USER"
],
"jti": "e0ad1ef3-a8a5-4eef-998d-00b26bc2c53f",
"client_id": "fooClientIdPassword"
}
5.1、在JS客戶端使用訪問令牌
最后,我們要在AngualrJS客戶端應(yīng)用程序中使用令牌信息。我們將使用angular-jwt庫。 所以我們要做的就是在index.html中使用“組織”聲明:
<p class="navbar-text navbar-right">{{organization}}</p>
<script type="text/javascript"
src="https://cdn.rawgit.com/auth0/angular-jwt/master/dist/angular-jwt.js">
</script>
<script>
var app = angular.module('myApp', ["ngResource","ngRoute", "ngCookies", "angular-jwt"]);
app.controller('mainCtrl', function($scope, $cookies, jwtHelper,...) {
$scope.organiztion = "";
function getOrganization(){
var token = $cookies.get("access_token");
var payload = jwtHelper.decodeToken(token);
$scope.organization = payload.organization;
}
...
});
6、不對稱的KeyPair
在我們以前的配置中,我們使用對稱密鑰來簽署我們的令牌:
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
我們還可以使用非對稱密鑰(公鑰和私鑰)來執(zhí)行簽名過程。
6.1、生成JKS Java KeyStore文件
我們首先使用命令行工具keytool生成密鑰 - 更具體地說.jks文件:
keytool -genkeypair -alias mytest
-keyalg RSA
-keypass mypass
-keystore mytest.jks
-storepass mypass
該命令將生成一個(gè)名為mytest.jks的文件,其中包含我們的密鑰 - 公鑰和私鑰。 還要確保keypass和storepass是一樣的。
6.2、導(dǎo)出公鑰
接下來,我們需要從生成的JKS中導(dǎo)出我們的公鑰,我們可以使用下面的命令來實(shí)現(xiàn):
keytool -list -rfc --keystore mytest.jks | openssl x509 -inform pem -pubkey
示例回應(yīng)如下所示:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp OsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2 /5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3 DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR xxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3Cr lAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK eQIDAQAB -----END PUBLIC KEY----- -----BEGIN CERTIFICATE----- MIIDCzCCAfOgAwIBAgIEGtZIUzANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJ1 czELMAkGA1UECBMCY2ExCzAJBgNVBAcTAmxhMQ0wCwYDVQQDEwR0ZXN0MB4XDTE2 MDMxNTA4MTAzMFoXDTE2MDYxMzA4MTAzMFowNjELMAkGA1UEBhMCdXMxCzAJBgNV BAgTAmNhMQswCQYDVQQHEwJsYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAICCtlreMdhLQ5eNQu736TrDKrmTMjsrXjtkbFXj Cxf4VyHmL4nCq9EkM1ZKHRxAQjIhl0A8+aa4o06t0Rz8tv+ViQQmKu8h4Ey77KTM urIr1zezXWBOyOaV6Pyh5OJ8/hWuj9y/Pi/dBP96sH+o9wylpwICRUWPAG0mF7dX eRC4iBtf4BKswtH2ZjYYX6wbccFl65aVA09Cn739EFZj0ccQi10/rRHtbHlhhKnj iy+b10S6ps2XAXtUWfZEEJuN/mvUJ+YnEkZw30wHrENwq5QFiSpdpHFlNR8CasPn WUUmdV+JBFzTMsz3TwWxplOjB3YacsCO0imU+5l+AQ51CnkCAwEAAaMhMB8wHQYD VR0OBBYEFOGefUBGquEX9Ujak34PyRskHk+WMA0GCSqGSIb3DQEBCwUAA4IBAQB3 1eLfNeq45yO1cXNl0C1IQLknP2WXg89AHEbKkUOA1ZKTOizNYJIHW5MYJU/zScu0 yBobhTDe5hDTsATMa9sN5CPOaLJwzpWV/ZC6WyhAWTfljzZC6d2rL3QYrSIRxmsp /J1Vq9WkesQdShnEGy7GgRgJn4A8CKecHSzqyzXulQ7Zah6GoEUD+vjb+BheP4aN hiYY1OuXD+HsdKeQqS+7eM5U7WW6dz2Q8mtFJ5qAxjY75T0pPrHwZMlJUhUZ+Q2V FfweJEaoNB9w9McPe1cAiE+oeejZ0jq0el3/dJsx3rlVqZN+lMhRJJeVHFyeb3XF lLFCUGhA7hxn2xf3x1JW -----END CERTIFICATE-----
我們只取得我們的公鑰,并將其復(fù)制到我們的資源服務(wù)器src / main / resources / public.txt中
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp OsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2 /5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3 DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR xxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3Cr lAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK eQIDAQAB -----END PUBLIC KEY-----
6.3、Maven 配置
接下來,我們不希望JMS文件被maven過濾進(jìn)程拾取 - 所以我們將確保將其排除在pom.xml中:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>*.jks</exclude>
</excludes>
</resource>
</resources>
</build>
如果我們使用Spring Boot,我們需要確保我們的JKS文件通過Spring Boot Maven插件添加到應(yīng)用程序classpath - addResources:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
6.4、授權(quán)服務(wù)器
現(xiàn)在,我們將配置JwtAccessTokenConverter使用mytest.jks中的KeyPair,如下所示:
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), "mypass".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
return converter;
}
6.5、資源服務(wù)器
最后,我們需要配置我們的資源服務(wù)器使用公鑰 - 如下所示:
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("public.txt");
String publicKey = null;
try {
publicKey = IOUtils.toString(resource.getInputStream());
} catch (final IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java如何使用itext向PDF插入數(shù)據(jù)和圖片
最近項(xiàng)目中使用到Java實(shí)現(xiàn)寫入PDF文件,這篇文章主要給大家介紹了關(guān)于Java如何使用itext向PDF插入數(shù)據(jù)和圖片的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
Java實(shí)現(xiàn)API sign簽名校驗(yàn)的方法詳解
為了防止中間人攻擊,有時(shí)我們需要進(jìn)行API sign 簽名校驗(yàn)。本文將用Java語言實(shí)現(xiàn)API sign 簽名校驗(yàn),感興趣的小伙伴可以嘗試一下2022-07-07
Java中讓界面內(nèi)的時(shí)間及時(shí)更新示例代碼
這篇文章主要給大家介紹了關(guān)于Java中讓界面內(nèi)的時(shí)間及時(shí)更新的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Java服務(wù)中的大文件上傳和下載優(yōu)化技巧分享
在Java服務(wù)中處理大文件的上傳和下載是一項(xiàng)常見但復(fù)雜的任務(wù),為了提供優(yōu)秀的用戶體驗(yàn)和高效的系統(tǒng)性能,我們將探索多種策略和技術(shù),并在每一點(diǎn)上都提供代碼示例以便實(shí)戰(zhàn)應(yīng)用,需要的朋友可以參考下2023-10-10
Mybatis批量插入index out of range錯(cuò)誤的解決(較偏的錯(cuò)誤)
這篇文章主要介紹了Mybatis批量插入index out of range錯(cuò)誤的解決(較偏的錯(cuò)誤),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
IDEA實(shí)現(xiàn)導(dǎo)入module并成功運(yùn)行
這篇文章主要介紹了IDEA實(shí)現(xiàn)導(dǎo)入module并成功運(yùn)行方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Java數(shù)組使用binarySearch()方法查找指定元素的實(shí)現(xiàn)
這篇文章主要介紹了Java數(shù)組使用binarySearch()方法查找指定元素的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

