Skip to content

Commit

Permalink
✨ Add spring-boot-demo-oauth-resource-server.
Browse files Browse the repository at this point in the history
  • Loading branch information
lizhongyue248 committed Jan 9, 2020
1 parent dcfeb4f commit a989d01
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 27 deletions.
26 changes: 1 addition & 25 deletions spring-boot-demo-oauth/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<version>1.0.0-SNAPSHOT</version>
<modules>
<module>spring-boot-demo-oauth-authorization-server</module>
<module>spring-boot-demo-oauth-resource-server</module>
</modules>
<packaging>pom</packaging>

Expand All @@ -26,31 +27,6 @@
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,34 @@

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

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ server:

spring:
datasource:
url: jdbc:mysql://localhost:3306/oauth
url: jdbc:mysql://localhost:3306/oauth?allowPublicKeyRetrieval=true
username: root
password: 123456
hikari:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<v-btn outlined color="info" @click="previous">{{previousText}}</v-btn>
<v-spacer></v-spacer>
<v-btn color="info" type="button" @click="next" v-show="window === 0">下一步</v-btn>
<v-btn color="info" type="submit" v-show="window === 1">登录</v-btn>
<v-btn color="info" type="submit" @click="next" v-show="window === 1">登录</v-btn>
</v-card-actions>
</v-form>
</v-card>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
= spring-boot-demo-oauth-resource-server
Doc Writer <lzy@echocow.cn>
v1.0, 2019-01-09
:toc:

spring boot oauth2 资源服务器,同 授权服务器 一起使用。

> 使用 `spring security oauth`

- JWT 解密,远程公钥获取
- 基于角色访问控制
- 基于应用授权域访问控制
== jwt 解密

要先获取 jwt 公钥

[source,java]
.OauthResourceTokenConfig
----
public class OauthResourceTokenConfig {
// ......
private String getPubKey() {
// 如果本地没有密钥,就从授权服务器中获取
return StringUtils.isEmpty(resourceServerProperties.getJwt().getKeyValue())
? getKeyFromAuthorizationServer()
: resourceServerProperties.getJwt().getKeyValue();
}
// ......
}
----

然后配置进去

[source, java]
.OauthResourceServerConfig
----
public class OauthResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.tokenStore(tokenStore)
.resourceId(resourceServerProperties.getResourceId());
}
}
----

== 访问控制

通过 `@EnableGlobalMethodSecurity(prePostEnabled = true)` 注解开启 `spring security` 的全局方法安全控制

- `@PreAuthorize("hasRole('ADMIN')")` 校验角色
- `@PreAuthorize("#oauth2.hasScope('READ')")` 校验令牌授权域

== 测试

测试用例: `com.xkcoding.oauth.controller.TestControllerTest`

先获取 `token`,携带 `token` 去访问资源即可。
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-boot-demo-oauth</artifactId>
<groupId>com.xkcoding</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-boot-demo-oauth-resource-server</artifactId>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.xkcoding.oauth;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

/**
* 启动器.
*
* @author <a href="https://echocow.cn">EchoCow</a>
* @date 2020/1/9 上午11:38
* @version V1.0
*/
@EnableResourceServer
@SpringBootApplication
public class SpringBootDemoResourceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoResourceApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.xkcoding.oauth.config;

import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
* 资源服务器配置.
* 我们自己实现了它的配置,所以它的自动装配不会生效
*
* @author <a href="https://echocow.cn">EchoCow</a>
* @date 2020/1/9 下午2:20
*/
@Configuration
@AllArgsConstructor
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OauthResourceServerConfig extends ResourceServerConfigurerAdapter {

private final ResourceServerProperties resourceServerProperties;
private final TokenStore tokenStore;

@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.tokenStore(tokenStore)
.resourceId(resourceServerProperties.getResourceId());
}

@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
// 前后端分离下,可以关闭 csrf
http.csrf().disable();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.xkcoding.oauth.config;

import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Base64;

/**
* token 相关配置,jwt 相关.
*
* @author <a href="https://echocow.cn">EchoCow</a>
* @date 2020/1/9 下午2:39
*/
@Slf4j
@Configuration
@AllArgsConstructor
public class OauthResourceTokenConfig {

private final ResourceServerProperties resourceServerProperties;

/**
* 这里并不是对令牌的存储,他将访问令牌与身份验证进行转换
* 在需要 {@link TokenStore} 的任何地方可以使用此方法
*
* @return TokenStore
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}

/**
* jwt 令牌转换
*
* @return jwt
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(getPubKey());
return converter;
}

/**
* 非对称密钥加密,获取 public key。
* 自动选择加载方式。
*
* @return public key
*/
private String getPubKey() {
// 如果本地没有密钥,就从授权服务器中获取
return StringUtils.isEmpty(resourceServerProperties.getJwt().getKeyValue())
? getKeyFromAuthorizationServer()
: resourceServerProperties.getJwt().getKeyValue();
}

/**
* 本地没有公钥的时候,从服务器上获取
* 需要进行 Basic 认证
*
* @return public key
*/
private String getKeyFromAuthorizationServer() {
ObjectMapper objectMapper = new ObjectMapper();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.AUTHORIZATION, encodeClient());
HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeaders);
String pubKey = new RestTemplate()
.getForObject(resourceServerProperties.getJwt().getKeyUri(), String.class, requestEntity);
try {
JSONObject body = objectMapper.readValue(pubKey, JSONObject.class);
log.info("Get Key From Authorization Server.");
return body.getStr("value");
} catch (IOException e) {
log.error("Get public key error: {}", e.getMessage());
}
return null;
}

/**
* 客户端信息
*
* @return basic
*/
private String encodeClient() {
return "Basic " + Base64.getEncoder().encodeToString((resourceServerProperties.getClientId()
+ ":" + resourceServerProperties.getClientSecret()).getBytes());
}
}
Loading

0 comments on commit a989d01

Please sign in to comment.