Skip to content

Commit a989d01

Browse files
committed
✨ Add spring-boot-demo-oauth-resource-server.
1 parent dcfeb4f commit a989d01

File tree

13 files changed

+499
-27
lines changed

13 files changed

+499
-27
lines changed

spring-boot-demo-oauth/pom.xml

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<version>1.0.0-SNAPSHOT</version>
88
<modules>
99
<module>spring-boot-demo-oauth-authorization-server</module>
10+
<module>spring-boot-demo-oauth-resource-server</module>
1011
</modules>
1112
<packaging>pom</packaging>
1213

@@ -26,31 +27,6 @@
2627
</properties>
2728

2829
<dependencies>
29-
<dependency>
30-
<groupId>org.springframework.boot</groupId>
31-
<artifactId>spring-boot-starter-web</artifactId>
32-
</dependency>
33-
34-
<dependency>
35-
<groupId>org.springframework.boot</groupId>
36-
<artifactId>spring-boot-starter-security</artifactId>
37-
</dependency>
38-
39-
<dependency>
40-
<groupId>org.springframework.boot</groupId>
41-
<artifactId>spring-boot-starter-thymeleaf</artifactId>
42-
</dependency>
43-
44-
<dependency>
45-
<groupId>org.springframework.boot</groupId>
46-
<artifactId>spring-boot-starter-data-jpa</artifactId>
47-
</dependency>
48-
49-
<dependency>
50-
<groupId>org.springframework.security.oauth.boot</groupId>
51-
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
52-
<version>${spring.boot.version}</version>
53-
</dependency>
5430

5531
<dependency>
5632
<groupId>mysql</groupId>

spring-boot-demo-oauth/spring-boot-demo-oauth-authorization-server/pom.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,34 @@
1111

1212
<artifactId>spring-boot-demo-oauth-authorization-server</artifactId>
1313

14+
<dependencies>
15+
16+
<dependency>
17+
<groupId>org.springframework.boot</groupId>
18+
<artifactId>spring-boot-starter-web</artifactId>
19+
</dependency>
20+
21+
<dependency>
22+
<groupId>org.springframework.boot</groupId>
23+
<artifactId>spring-boot-starter-security</artifactId>
24+
</dependency>
25+
26+
<dependency>
27+
<groupId>org.springframework.security.oauth.boot</groupId>
28+
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
29+
<version>${spring.boot.version}</version>
30+
</dependency>
31+
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
35+
</dependency>
36+
37+
<dependency>
38+
<groupId>org.springframework.boot</groupId>
39+
<artifactId>spring-boot-starter-data-jpa</artifactId>
40+
</dependency>
41+
42+
</dependencies>
1443

1544
</project>

spring-boot-demo-oauth/spring-boot-demo-oauth-authorization-server/src/main/resources/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ server:
33

44
spring:
55
datasource:
6-
url: jdbc:mysql://localhost:3306/oauth
6+
url: jdbc:mysql://localhost:3306/oauth?allowPublicKeyRetrieval=true
77
username: root
88
password: 123456
99
hikari:

spring-boot-demo-oauth/spring-boot-demo-oauth-authorization-server/src/main/resources/templates/login.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<v-btn outlined color="info" @click="previous">{{previousText}}</v-btn>
4444
<v-spacer></v-spacer>
4545
<v-btn color="info" type="button" @click="next" v-show="window === 0">下一步</v-btn>
46-
<v-btn color="info" type="submit" v-show="window === 1">登录</v-btn>
46+
<v-btn color="info" type="submit" @click="next" v-show="window === 1">登录</v-btn>
4747
</v-card-actions>
4848
</v-form>
4949
</v-card>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
= spring-boot-demo-oauth-resource-server
2+
Doc Writer <lzy@echocow.cn>
3+
v1.0, 2019-01-09
4+
:toc:
5+
6+
spring boot oauth2 资源服务器,同 授权服务器 一起使用。
7+
8+
> 使用 `spring security oauth`
9+
10+
- JWT 解密,远程公钥获取
11+
- 基于角色访问控制
12+
- 基于应用授权域访问控制
13+
14+
== jwt 解密
15+
16+
要先获取 jwt 公钥
17+
18+
[source,java]
19+
.OauthResourceTokenConfig
20+
----
21+
public class OauthResourceTokenConfig {
22+
// ......
23+
private String getPubKey() {
24+
// 如果本地没有密钥,就从授权服务器中获取
25+
return StringUtils.isEmpty(resourceServerProperties.getJwt().getKeyValue())
26+
? getKeyFromAuthorizationServer()
27+
: resourceServerProperties.getJwt().getKeyValue();
28+
}
29+
// ......
30+
}
31+
----
32+
33+
然后配置进去
34+
35+
[source, java]
36+
.OauthResourceServerConfig
37+
----
38+
public class OauthResourceServerConfig extends ResourceServerConfigurerAdapter {
39+
@Override
40+
public void configure(ResourceServerSecurityConfigurer resources) {
41+
resources
42+
.tokenStore(tokenStore)
43+
.resourceId(resourceServerProperties.getResourceId());
44+
}
45+
}
46+
----
47+
48+
== 访问控制
49+
50+
通过 `@EnableGlobalMethodSecurity(prePostEnabled = true)` 注解开启 `spring security` 的全局方法安全控制
51+
52+
- `@PreAuthorize("hasRole('ADMIN')")` 校验角色
53+
- `@PreAuthorize("#oauth2.hasScope('READ')")` 校验令牌授权域
54+
55+
== 测试
56+
57+
测试用例: `com.xkcoding.oauth.controller.TestControllerTest`
58+
59+
先获取 `token`,携带 `token` 去访问资源即可。
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>spring-boot-demo-oauth</artifactId>
7+
<groupId>com.xkcoding</groupId>
8+
<version>1.0.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>spring-boot-demo-oauth-resource-server</artifactId>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-web</artifactId>
18+
</dependency>
19+
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-starter-security</artifactId>
23+
</dependency>
24+
25+
<dependency>
26+
<groupId>org.springframework.security.oauth.boot</groupId>
27+
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
28+
<version>${spring.boot.version}</version>
29+
</dependency>
30+
</dependencies>
31+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.xkcoding.oauth;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
6+
7+
/**
8+
* 启动器.
9+
*
10+
* @author <a href="https://echocow.cn">EchoCow</a>
11+
* @date 2020/1/9 上午11:38
12+
* @version V1.0
13+
*/
14+
@EnableResourceServer
15+
@SpringBootApplication
16+
public class SpringBootDemoResourceApplication {
17+
public static void main(String[] args) {
18+
SpringApplication.run(SpringBootDemoResourceApplication.class, args);
19+
}
20+
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.xkcoding.oauth.config;
2+
3+
import lombok.AllArgsConstructor;
4+
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
7+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
8+
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
9+
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
10+
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
11+
import org.springframework.security.oauth2.provider.token.TokenStore;
12+
13+
/**
14+
* 资源服务器配置.
15+
* 我们自己实现了它的配置,所以它的自动装配不会生效
16+
*
17+
* @author <a href="https://echocow.cn">EchoCow</a>
18+
* @date 2020/1/9 下午2:20
19+
*/
20+
@Configuration
21+
@AllArgsConstructor
22+
@EnableResourceServer
23+
@EnableGlobalMethodSecurity(prePostEnabled = true)
24+
public class OauthResourceServerConfig extends ResourceServerConfigurerAdapter {
25+
26+
private final ResourceServerProperties resourceServerProperties;
27+
private final TokenStore tokenStore;
28+
29+
@Override
30+
public void configure(ResourceServerSecurityConfigurer resources) {
31+
resources
32+
.tokenStore(tokenStore)
33+
.resourceId(resourceServerProperties.getResourceId());
34+
}
35+
36+
@Override
37+
public void configure(HttpSecurity http) throws Exception {
38+
super.configure(http);
39+
// 前后端分离下,可以关闭 csrf
40+
http.csrf().disable();
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.xkcoding.oauth.config;
2+
3+
import cn.hutool.json.JSONObject;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import lombok.AllArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.http.HttpEntity;
11+
import org.springframework.http.HttpHeaders;
12+
import org.springframework.security.oauth2.provider.token.TokenStore;
13+
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
14+
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
15+
import org.springframework.util.StringUtils;
16+
import org.springframework.web.client.RestTemplate;
17+
18+
import java.io.IOException;
19+
import java.util.Base64;
20+
21+
/**
22+
* token 相关配置,jwt 相关.
23+
*
24+
* @author <a href="https://echocow.cn">EchoCow</a>
25+
* @date 2020/1/9 下午2:39
26+
*/
27+
@Slf4j
28+
@Configuration
29+
@AllArgsConstructor
30+
public class OauthResourceTokenConfig {
31+
32+
private final ResourceServerProperties resourceServerProperties;
33+
34+
/**
35+
* 这里并不是对令牌的存储,他将访问令牌与身份验证进行转换
36+
* 在需要 {@link TokenStore} 的任何地方可以使用此方法
37+
*
38+
* @return TokenStore
39+
*/
40+
@Bean
41+
public TokenStore tokenStore() {
42+
return new JwtTokenStore(jwtAccessTokenConverter());
43+
}
44+
45+
/**
46+
* jwt 令牌转换
47+
*
48+
* @return jwt
49+
*/
50+
@Bean
51+
public JwtAccessTokenConverter jwtAccessTokenConverter() {
52+
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
53+
converter.setVerifierKey(getPubKey());
54+
return converter;
55+
}
56+
57+
/**
58+
* 非对称密钥加密,获取 public key。
59+
* 自动选择加载方式。
60+
*
61+
* @return public key
62+
*/
63+
private String getPubKey() {
64+
// 如果本地没有密钥,就从授权服务器中获取
65+
return StringUtils.isEmpty(resourceServerProperties.getJwt().getKeyValue())
66+
? getKeyFromAuthorizationServer()
67+
: resourceServerProperties.getJwt().getKeyValue();
68+
}
69+
70+
/**
71+
* 本地没有公钥的时候,从服务器上获取
72+
* 需要进行 Basic 认证
73+
*
74+
* @return public key
75+
*/
76+
private String getKeyFromAuthorizationServer() {
77+
ObjectMapper objectMapper = new ObjectMapper();
78+
HttpHeaders httpHeaders = new HttpHeaders();
79+
httpHeaders.add(HttpHeaders.AUTHORIZATION, encodeClient());
80+
HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeaders);
81+
String pubKey = new RestTemplate()
82+
.getForObject(resourceServerProperties.getJwt().getKeyUri(), String.class, requestEntity);
83+
try {
84+
JSONObject body = objectMapper.readValue(pubKey, JSONObject.class);
85+
log.info("Get Key From Authorization Server.");
86+
return body.getStr("value");
87+
} catch (IOException e) {
88+
log.error("Get public key error: {}", e.getMessage());
89+
}
90+
return null;
91+
}
92+
93+
/**
94+
* 客户端信息
95+
*
96+
* @return basic
97+
*/
98+
private String encodeClient() {
99+
return "Basic " + Base64.getEncoder().encodeToString((resourceServerProperties.getClientId()
100+
+ ":" + resourceServerProperties.getClientSecret()).getBytes());
101+
}
102+
}

0 commit comments

Comments
 (0)