[home]开源官网初始化

This commit is contained in:
tomsun28
2022-02-02 22:49:50 +08:00
parent 9347097905
commit c47706be17
132 changed files with 15971 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
---
id: annotation-datasource
title: 注解形式的资源权限数据源
sidebar_label: 注解权限数据源
---
`sureness`认证鉴权,当然也需要我们提供自己的账户数据,角色权限数据等,这些数据可能来自文本,关系数据库,非关系数据库,注解等。
我们提供了数据源接口:`SurenessAccountProvider` - 账户数据接口, `PathTreeProvider` - 资源权限数据接口,用户可以实现此接口实现自定义数据源。
`sureness`实现注解权限的方式不是调用方法前aop判断而是启动时扫描注解里的数据作为权限数据源来使用这样方便了流程统一和框架无关性。
这里介绍下注解形式的权限数据源配置方法。
1. 首先我们需要在sureness启动配置中配置使用注解数据源作为权限数据源。
```
@Bean
TreePathRoleMatcher pathRoleMatcher() {
// 实例化资源权限路径匹配者,其会根据请求的路径和已有的资源权限数据匹配出所需的角色信息
DefaultPathRoleMatcher pathRoleMatcher = new DefaultPathRoleMatcher();
// 实例化注解形式的资源权限数据加载者AnnotationLoader其实现了PathTreeProvider接口
AnnotationPathTreeProvider pathTreeProvider = new AnnotationPathTreeProvider();
// 设置AnnotationLoader要扫描的包路径其会扫描包路径下所有类方法上的@RequiresRoles, @WithoutAuth 注解获取数据
pathTreeProvider.setScanPackages(Arrays.asList("com.usthe.sureness.sample.tom.controller"));
// 将AnnotationLoader数据源设置为sureness的权限资源数据源
pathRoleMatcher.addPathTreeProvider(pathTreeProvider);
pathRoleMatcher.buildTree();
return pathRoleMatcher;
}
```
2. 在提供的接口方法中使用注解,注解使用格式:
```
@RequiresRoles(roles = {"role1", "role2"}, mapping = "/resource", method = "post")
其表示资源 /resource===post 的需要角色 role1或者role2才能访问
```
```
@WithoutAuth(mapping = "/resource/*", method = "put")
其表示资源 /resource/*===put 的可以被任何请求访问
```
3. 建议。
注解形式的权限数据源虽然比较方便我们开发,但其写死在代码中,无法动态修改权限角色配置数据,对于大型项目反而不是很适用。
`sureness`提供了多个数据源同时加载的功能即我们可以同时将注解形式的权限数据源和数据库里的配置数据作为数据源加载到sureness配置中
对于不常修改的权限配置,我们可以将其配置到注解,对于其他需要动态修改的权限数据,我们就将其配置到数据库中。
当然也我们提供了默认文本数据源,默认文本数据源具体实现,请参考 [默认文本数据源](/docs/start/default-datasource)
数据源也可以来自数据库等存储,我们提供了接口让用户轻松的自定义数据源,详见[自定义数据源](/docs/advanced/custom-datasource)

View File

@@ -0,0 +1,51 @@
---
id: default-auth
title: 默认支持的认证方式
sidebar_label: 默认认证方式
---
`sureness`目前默认支持的认证方式有`bearer jwt`,`basic auth`, `digest auth`, 当然用户可以通过扩展`Processor`,`Subject``SubjectCreate`接口实现自定义的认证方式
#### `bearer jwt`
`jwt``json web token`,是目前很流行的跨域,无状态,安全认证解决方案,介绍详见[网络](http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
我们这里为啥叫`bearer jwt`是因为`jwt`是放入到http请求头的`bearer token`里面,即: `Authorization: Bearer jsonWebTokenValue`
eg:
```
GET /api/v1/source1 HTTP/1.1
Host: localhost:8088
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInppcCI6IkRFRiJ9.eNocjEEOwiAQRe8y65IwCBQ4hlvjotAhVqs1DBoT492l7F5e_vtfuNYFAliUPs3aCrIuCW1nFDHlUaBVqJOLJpkIA_ArtnHd7o0X5s43egim8qayy6lCQOOUd15JHIA-zy4OUo5dlG2lFp46KDjvR0fKhfgCIU8r0-8PAAD__w.f-3klWWDpEO3uDLlx2S53DV2cYernwVEDwcC6z1JexocbZoxRKmASTOuky1qMCxy_hV8-RbuMjDmI3ASa_FQOw
```
我们可以在`postman`如下使用它: 将`jwt`值塞入`Bearer Token`里.
![jwtPostmanUse](/img/docs/jwtPostmanUse.png)
#### `basic auth`
`basic auth`即`Basic access authentication`,经典的`http`基本认证方式,介绍详见[网络](https://www.jianshu.com/p/4cd42f7359f4)
这种认证方式是将账户密码组成的字符串`base64`加密,放入到请求头的 `Authorization`中, 即:`Authorization: Basic base64encode(username+":"+password)`
eg:
```
GET /api/v1/source1 HTTP/1.1
Host: localhost:8088
Content-Type: application/json
Authorization: Basic dG9tOjMyMTEz
```
我们可以在`postman`如下使用它: 在`Basic Auth`类型的`Authorization`中输入账户密码即可,`postman`会自动对其`base64`加密.
![basicAuthPostmanUse](/img/docs/basicAuthPostmanUse.png)
#### `digest auth`
`digest auth`即`Digest access authentication`,经典的`http`摘要认证方式,用于保护传输的密码,介绍详见[网络](https://www.cnblogs.com/xiaoxiaotank/p/11078571.html)
下面是`digest auth`的认证流程(图片来源于[网络](https://www.cnblogs.com/xiaoxiaotank/p/11078571.html)):
![digestFlow](/img/docs/digestFlow.png)
我们可以在`chrome`浏览器直接使用它: 访问`url`,在弹出的对话框中输入账户密码即可,`chrome`浏览器会自动进行认证流程.
![digestAuthChromeUse](/img/docs/digestAuthUse.png)
#### 其他认证方式
目前`sureness`默认支持这三种主流的认证方式,满足绝大部分需求,当然你也可以很轻松的自定义认证方式,详见[自定义Subject](/docs/advanced/custom-subject)
我们提供了默认认证方式的使用`DEMO`,请参考 [一步一步搭建认证鉴权系统](/docs/integrate/sample-bootstrap)
当然我们也提供了自定义认证方式的扩展`DEMO`,请参考 [Springboot项目集成-数据库方案](/docs/integrate/sample-tom)

View File

@@ -0,0 +1,64 @@
---
id: default-datasource
title: 默认文本配置数据源
sidebar_label: 默认文本数据源
---
`sureness`认证鉴权当然也需要我们自己的配置数据:账户数据,角色权限数据等
这些配置数据可能来自文本,关系数据库,非关系数据库
我们提供了配置数据接口`SurenessAccountProvider`, `PathTreeProvider`, 用户可以实现此接口实现自定义配置数据源
当然我们也提供默认文本形式的配置数据实现 `DocumentResourceDefaultProvider`
用户可以创建文件`sureness.yml`来配置数据,配置样例如下:
```
## -- sureness.yml文本数据源 -- ##
# 加载到匹配字典的资源,也就是需要被保护的,设置了所支持角色访问的资源
# 没有配置的资源也默认被认证保护,但不鉴权
# eg: /api/v1/source1===get===[role2] 表示 /api/v2/host===post 这条资源支持 role2 这一种角色访问
# eg: /api/v1/source2===get===[] 表示 /api/v1/source2===get 这条资源不支持任何角色访问
resourceRole:
- /api/v1/source1===get===[role2]
- /api/v1/source1===delete===[role3]
- /api/v1/source1===put===[role1,role2]
- /api/v1/source2===get===[]
- /api/v1/source2/*/*===get===[role2]
- /api/v2/source3/*===get===[role2]
- /api/v3/source===*===[role2]
# 需要被过滤保护的资源,不认证鉴权直接访问
# /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权
excludedResource:
- /api/v1/account/auth===post
- /api/v1/source3===get
- /**/*.html===get
- /**/*.js===get
- /**/*.css===get
- /**/*.ico===get
- /**/*.png===*
# 用户账户信息
# 下面有 admin root tom三个账户
# eg: admin 拥有[role1,role2]角色,明文密码为admin,加盐密码为0192023A7BBD73250516F069DF18B500
# eg: root 拥有[role1],密码为明文23456
# eg: tom 拥有[role3],密码为明文32113
account:
- appId: admin
# 如果填写了加密盐--salt,则credential为MD5(password+salt)的32位结果
# 没有盐认为不加密,credential为明文
# 若对密码加盐 则不支持digest认证
credential: 0192023A7BBD73250516F069DF18B500
salt: 123
role: [role1,role2]
- appId: root
credential: 23456
role: [role1]
- appId: tom
credential: 32113
role: [role3]
```
我们提供了默认文本数据源使用`DEMO`,默认文本数据源具体实现,请参考 [一步一步搭建认证鉴权系统](/docs/integrate/sample-bootstrap)
当然数据源也可以来自数据库等存储,我们提供了接口让用户轻松的自定义数据源,详见[自定义数据源](/docs/advanced/custom-datasource)

View File

@@ -0,0 +1,45 @@
---
id: default-exception
title: sureness 默认认证鉴权异常
sidebar_label: 默认异常类型
---
`sureness`使用异常处理流程:
1. 若认证鉴权成功,`checkIn`会返回包含用户信息的`SubjectSum`对象
2. 若中间认证鉴权失败,`checkIn`会抛出不同类型的认证鉴权异常,用户需根据这些异常来继续后面的流程(返回相应的请求响应)
这里我们就需要对`checkIn`抛出的异常做自定义处理,认证鉴权成功直接通过,失败抛出特定异常进行处理,如下:
```
try {
SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
} catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
// 账户创建相关异常
} catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
// 账户禁用相关异常
} catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
// 认证失败相关异常
} catch (UnauthorizedException e5) {
// 鉴权失败相关异常
} catch (RuntimeException e) {
// 其他自定义异常
}
```
sureness 默认支持的异常处理流程中的异常如下:
`sureness`异常 | 异常描述
--- | ---
SurenessAuthenticationException | 基础认证异常,认证相关的子异常应该继承此异常
SurenessAuthorizationException | 基础鉴权异常,鉴权相关的子异常应该继承此异常
ProcessorNotFoundException | 认证异常,未找到支持此subject的processor
UnknownAccountException | 认证异常,不存在此账户
UnSupportedSubjectException | 认证异常,不支持的请求,未创建出subject
DisabledAccountException | 认证异常,账户禁用
ExcessiveAttemptsException | 认证异常,账户尝试认证次数过多
IncrrectCredentialsException | 认证异常,密钥错误
ExpiredCredentialsException | 认证异常,密钥认证过期
NeedDigestInfoException | 认证异常, 通过getAuthenticate()返回客户端digest认证所需信息
UnauthorizedException | 鉴权异常,没有权限访问此资源
自定义异常需要继承`SurenessAuthenticationException``SurenessAuthorizationException`才能被最外层捕获

View File

@@ -0,0 +1,27 @@
---
id: path-match
title: URI路径匹配
sidebar_label: URI路径匹配
---
我们配置的资源格式为:`requestUri===httpMethod`, 即请求的路径加上其请求方式(`post,get,put,delete...或者*,*匹配所有请求方式`)作为一个整体被视作一个资源
`eg: /api/v2/book===get` `get`方式请求`/api/v2/book`接口数据
这里的`requestUri`支持url路径匹配符匹配: `str*str`, `*`, `**`
| 通配符 | 描述 |
| --- | --- |
| `str*str` | 字符串中的*匹配0个或者多个任意字符 |
| `*` | 匹配0个或1个目录 |
| `**` | 匹配0个或多个目录 |
| 样例 | 说明 |
| --- | --- |
| `*.html` | 可以匹配 `content.html`, `user-ui.html` 等 |
| `/api/*/book` | 可以匹配 `/api/user/book``/api/book` 等 |
| `/**` | 可以匹配任何路径 |
| `/**/foo` | 可以匹配 `/api/user/book/foo` 等 |
匹配优先级: 原始字符串 > `str*str` > `*` > `**`
最长路径匹配原则:
eg: `requestUri``/app/book/foo`,若存在两个路径匹配模式`/app/**``/app/book/*`,则会匹配到`/app/book/*`

View File

@@ -0,0 +1,93 @@
---
id: quickstart
title: 快速开始
sidebar_label: 快速开始
---
#### 🐕 使用前一些约定
- `Sureness`基于`RBAC`,即用户-角色-资源: 用户所属角色--角色拥有资源(API)--用户就能访问资源(API)
- 我们将`REST API`请求视作一个资源,资源格式为: `requestUri===httpMethod`
即请求的路径加上其请求方式(`post,get,put,delete...`)作为一个整体被视作资源来赋权配置
`eg: /api/v2/book===get` `get`方式请求`/api/v2/book`接口数据
资源路径匹配详见 [url路径匹配](/docs/start/path-match)
#### 项目中加入Sureness
项目使用`maven``gradle`构建,加入坐标
```
<dependency>
<groupId>com.usthe.sureness</groupId>
<artifactId>sureness-core</artifactId>
<version>1.0.6</version>
</dependency>
```
```
compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.6'
```
#### 🐵 使用默认配置来配置Sureness
默认配置使用了文件数据源`sureness.yml`作为账户权限数据源
默认配置支持了`JWT, Basic auth, Digest auth`认证
```
@Bean
public DefaultSurenessConfig surenessConfig() {
return new DefaultSurenessConfig();
}
```
#### 配置权限账户数据源
`Sureness`认证鉴权,当然也需要我们提供自己的账户数据,角色权限数据等,这些数据可能来自文本,关系数据库,非关系数据库,注解等。
我们提供了数据源接口:`SurenessAccountProvider`, `PathTreeProvider`,用户可以实现此接口实现自定义数据源。
当前我们也提供文本形式的数据源实现 `DocumentResourceDefaultProvider` 和 注解形式的资源权限数据源实现 `AnnotationLoader`
如果是使用了[默认sureness配置-DefaultSurenessConfig](#使用默认配置来配置sureness),其配置的是文本数据源,用户可以直接通过修改`sureness.yml`文件来配置数据。
文本数据源`sureness.yml`配置使用方式详见文档 [默认文本数据源](/docs/start/default-datasource)
注解形式的资源权限数据源配置使用方式详见文档 [注解资源权限数据源](/docs/start/annotation-datasource)
我们提供了使用代码`DEMO`
默认文本数据源具体实现,请参考[Sureness集成Spring Boot样例(配置文件方案)--sample-bootstrap](https://github.com/tomsun28/sureness/tree/master/sample-bootstrap)
若权限配置数据来自数据库,请参考[Sureness集成Spring Boot样例(数据库方案)--sample-tom](https://github.com/tomsun28/sureness/tree/master/sample-tom)
#### 添加过滤器拦截所有请求
`Sureness`的本质就拦截所有`API`请求对其认证鉴权判断。
入口拦截器器实现一般可以是 `filter or spring interceptor`
在拦截器中加入`Sureness`的安全过滤器,如下:
```
SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest)
```
#### 实现认证鉴权相关异常处理流程
`Sureness`使用异常处理流程:
1. 若认证鉴权成功,`checkIn`会返回包含用户信息的`SubjectSum`对象
2. 若中间认证鉴权失败,`checkIn`会抛出不同类型的认证鉴权异常,用户需根据这些异常来继续后面的流程(返回相应的请求响应)
这里我们就需要对`checkIn`抛出的异常做自定义处理,认证鉴权成功直接通过,失败抛出特定异常进行处理,如下:
```
try {
SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
} catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
// 账户创建相关异常
} catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
// 账户禁用相关异常
} catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
// 认证失败相关异常
} catch (UnauthorizedException e5) {
// 鉴权失败相关异常
} catch (SurenessAuthenticationException | SurenessAuthorizationException e) {
// 其他自定义异常
}
```
异常详见 [默认异常类型](/docs/start/default-exception)
**HAVE FUN**
> 如果这个[快速开始]对您不是很友好,可以参考下面一篇[一步一步搭建](https://juejin.cn/post/6921262609731682318)里面一步一步详细介绍了使用Sureness搭建一个完整功能认证鉴权项目的步骤。

View File

@@ -0,0 +1,432 @@
---
id: quickstart
title: 一步一步搭建
sidebar_label: 一步一步搭建
---
下面我们来一步一步基于springboot,sureness搭建一个如下功能的认证鉴权系统。
1. 使用了配置文件来作为系统的账户数据和权限数据的数据源。
2. 系统基于rbac权限模型支持basic认证digest认证jwt认证。
3. 能细粒度的控制用户对系统提供的restful api的访问权限即哪些用户能访问哪些api。
多说无益,快速开始!
这里为了照顾到刚入门的同学,图文展示了每一步操作。有基础可直接略过。
### 初始化一个springboot web工程
在IDEA如下操作:
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c2bc0a723ea74c86a75952cc486367cb~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/00d3cd3a015b4a079c0b30da064139d0~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3be963ed9c28493d9ddc3b48caab54af~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2bcce3d56cb244b6967914a5b9b807c5~tplv-k3u1fbpfcp-zoom-1.image)
### 提供一些模拟的restful api
新建一个controller, 在里面实现一些简单的restful api供外部测试调用
````
/**
* simulate api controller, for testing
* @author tomsun28
* @date 17:35 2019-05-12
*/
@RestController
public class SimulateController {
/** access success message **/
public static final String SUCCESS_ACCESS_RESOURCE = "access this resource success";
@GetMapping("/api/v1/source1")
public ResponseEntity<String> api1Mock1() {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@PutMapping("/api/v1/source1")
public ResponseEntity<String> api1Mock3() {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@DeleteMapping("/api/v1/source1")
public ResponseEntity<String> api1Mock4() {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@GetMapping("/api/v1/source2")
public ResponseEntity<String> api1Mock5() {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@GetMapping("/api/v1/source2/{var1}/{var2}")
public ResponseEntity<String> api1Mock6(@PathVariable String var1, @PathVariable Integer var2 ) {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@PostMapping("/api/v2/source3/{var1}")
public ResponseEntity<String> api1Mock7(@PathVariable String var1) {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@GetMapping("/api/v1/source3")
public ResponseEntity<String> api1Mock11(HttpServletRequest request) {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
@GetMapping("/api/v2/source2")
public ResponseEntity<String> api1Mock12() {
return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
}
}
````
### 项目中加入sureness依赖
在项目的pom.xml加入sureness的maven依赖坐标
```
<dependency>
<groupId>com.usthe.sureness</groupId>
<artifactId>sureness-core</artifactId>
<version>1.0.3</version>
</dependency>
```
如下:
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c0df60e4bae4d4b80c0209f461fc8b5~tplv-k3u1fbpfcp-zoom-1.image)
### 使用默认配置来配置sureness
新建一个配置类创建对应的sureness默认配置bean
sureness默认配置使用了文件数据源`sureness.yml`作为账户权限数据源
默认配置支持了`jwt, basic auth, digest auth`认证
```
@Configuration
public class SurenessConfiguration {
/**
* sureness default config bean
* @return default config bean
*/
@Bean
public DefaultSurenessConfig surenessConfig() {
return new DefaultSurenessConfig();
}
}
```
### 配置默认文本配置数据源
认证鉴权当然也需要我们自己的配置数据:账户数据,角色权限数据等
这些配置数据可能来自文本,关系数据库,非关系数据库
我们这里使用默认的文本形式配置 - sureness.yml, 在resource资源目录下创建sureness.yml文件
在sureness.yml文件里配置我们的角色权限数据和账户数据如下
````
## -- sureness.yml文本数据源 -- ##
# 加载到匹配字典的资源,也就是需要被保护的,设置了所支持角色访问的资源
# 没有配置的资源也默认被认证保护,但不鉴权,例如/api/v1/source2===get
# eg: /api/v1/source1===get===[role2] 表示 /api/v1/source1===get 这条资源支持 role2这一种角色访问
# eg: /api/v2/source2===get===[] 表示 /api/v1/source2===get 这条资源不支持任何角色访问
resourceRole:
- /api/v1/source1===get===[role2]
- /api/v1/source1===delete===[role3]
- /api/v1/source1===put===[role1,role2]
- /api/v2/source2===get===[]
- /api/v1/source2/*/*===get===[role2]
- /api/v2/source3/*===get===[role2]
# 需要被过滤保护的资源,不认证鉴权直接访问
# /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权
excludedResource:
- /api/v1/account/auth===post
- /api/v1/source3===get
- /**/*.html===get
- /**/*.js===get
- /**/*.css===get
- /**/*.ico===get
# 用户账户信息
# 下面有 admin root tom三个账户
# eg: admin 拥有[role1,role2]角色,明文密码为admin,加盐密码为0192023A7BBD73250516F069DF18B500
# eg: root 拥有[role1],密码为明文23456
# eg: tom 拥有[role3],密码为明文32113
account:
- appId: admin
# 如果填写了加密盐--salt,则credential为MD5(password+salt)的32位结果
# 没有盐认为不加密,credential为明文
# 若密码加盐 则digest认证不支持
credential: 0192023A7BBD73250516F069DF18B500
salt: 123
role: [role1,role2]
- appId: root
credential: 23456
role: [role1]
- appId: tom
credential: 32113
role: [role3]
````
### 添加过滤器拦截所有请求,对所有请求进行认证鉴权
新建一个filter, 拦截所有请求用sureness对所有请求进行认证鉴权。认证鉴权失败的请求sureness会抛出对应的异常我们捕获响应的异常进行处理返回response即可。
````
@Order(1)
@WebFilter(filterName = "SurenessFilterExample", urlPatterns = "/*", asyncSupported = true)
public class SurenessFilterExample implements Filter {
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
// 认证鉴权成功则会返回带用户信息的subject 可以将subject信息绑定到当前线程上下文holder供后面使用
if (subject != null) {
SurenessContextHolder.bindSubject(subject);
}
} catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
// 账户创建相关异常
responseWrite(ResponseEntity
.status(HttpStatus.BAD_REQUEST).body(e4.getMessage()), servletResponse);
return;
} catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
// 账户禁用相关异常
responseWrite(ResponseEntity
.status(HttpStatus.UNAUTHORIZED).body(e2.getMessage()), servletResponse);
return;
} catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
// 认证失败相关异常
responseWrite(ResponseEntity
.status(HttpStatus.UNAUTHORIZED).body(e3.getMessage()), servletResponse);
return;
} catch (NeedDigestInfoException e5) {
// digest认证需要重试异常
responseWrite(ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.header("WWW-Authenticate", e5.getAuthenticate()).build(), servletResponse);
return;
} catch (UnauthorizedException e6) {
// 鉴权失败相关异常即无权访问此api
responseWrite(ResponseEntity
.status(HttpStatus.FORBIDDEN).body(e6.getMessage()), servletResponse);
return;
} catch (RuntimeException e) {
// 其他异常
responseWrite(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(),
servletResponse);
return;
}
try {
// 若未抛出异常 则认证鉴权成功 继续下面请求流程
filterChain.doFilter(servletRequest, servletResponse);
} finally {
SurenessContextHolder.clear();
}
}
/**
* write response json data
* @param content content
* @param response response
*/
private static void responseWrite(ResponseEntity content, ServletResponse response) {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
((HttpServletResponse)response).setStatus(content.getStatusCodeValue());
content.getHeaders().forEach((key, value) ->
((HttpServletResponse) response).addHeader(key, value.get(0)));
try (PrintWriter printWriter = response.getWriter()) {
if (content.getBody() != null) {
if (content.getBody() instanceof String) {
printWriter.write(content.getBody().toString());
} else {
ObjectMapper objectMapper = new ObjectMapper();
printWriter.write(objectMapper.writeValueAsString(content.getBody()));
}
} else {
printWriter.flush();
}
} catch (IOException e) {}
}
}
````
像上面一样,
1. 若认证鉴权成功,`checkIn`会返回包含用户信息的`SubjectSum`对象
2. 若中间认证鉴权失败,`checkIn`会抛出不同类型的认证鉴权异常,用户需根据这些异常来继续后面的流程(返回相应的请求响应)
为了使filter在springboot生效 需要在boot启动类加注解 `@ServletComponentScan`
````
@SpringBootApplication
@ServletComponentScan
public class BootstrapApplication {
public static void main(String[] args) {
SpringApplication.run(BootstrapApplication.class, args);
}
}
````
### 一切完毕,验证测试
通过上面的步骤 我们的一个完整功能认证鉴权项目就搭建完成了,有同学想 就这几步骤 它的完整功能体现在哪里啊 能支持啥。
这个搭好的认证鉴权项目基于rbac权限模型支持 baisc 认证digest认证, jwt认证。能细粒度的控制用户对后台提供的restful api的访问权限即哪些用户能访问哪些api。 我们这里来测试一下。
IDEA上启动工程项目。
##### basic认证测试
资源api/v1/source2===get没有配置到文本数据源里代表所有角色或无角色都可以访问 前提是认证成功,用该资源来做认证测试
**认证成功**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a09f6caa4ef845adb9f5b5fa6e86040b~tplv-k3u1fbpfcp-zoom-1.image)
**密码错误**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/caa656a9a9174df6afc8768dc859ecb6~tplv-k3u1fbpfcp-zoom-1.image)
**账户不存在**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/37d8c0d0cd144b4fb8cfd5e0a47e6961~tplv-k3u1fbpfcp-zoom-1.image)
##### digest认证测试
**注意如果密码配置了加密盐则无法使用digest认证**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53fe5e08301c4171b31b6180a77b5837~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c12598db019549efa5f990c0136e7582~tplv-k3u1fbpfcp-zoom-1.image)
##### jwt认证测试
jwt认证首先你得拥有一个签发的jwt创建如下api接口提供jwt签发- `/api/v1/account/auth`
````
@RestController()
public class AccountController {
private static final String APP_ID = "appId";
/**
* account data provider
*/
private SurenessAccountProvider accountProvider = new DocumentAccountProvider();
/**
* login, this provider a get jwt api, convenient to test other api with jwt
* @param requestBody request
* @return response
*
*/
@PostMapping("/api/v1/account/auth")
public ResponseEntity<Object> login(@RequestBody Map<String,String> requestBody) {
if (requestBody == null || !requestBody.containsKey(APP_ID)
|| !requestBody.containsKey("password")) {
return ResponseEntity.badRequest().build();
}
String appId = requestBody.get("appId");
String password = requestBody.get("password");
SurenessAccount account = accountProvider.loadAccount(appId);
if (account == null || account.isDisabledAccount() || account.isExcessiveAttempts()) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
if (account.getPassword() != null) {
if (account.getSalt() != null) {
password = Md5Util.md5(password + account.getSalt());
}
if (!account.getPassword().equals(password)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
}
// Get the roles the user has - rbac
List<String> roles = account.getOwnRoles();
long refreshPeriodTime = 36000L;
// issue jwt
String jwt = JsonWebTokenUtil.issueJwt(UUID.randomUUID().toString(), appId,
"token-server", refreshPeriodTime >> 1, roles,
null, Boolean.FALSE);
Map<String, String> body = Collections.singletonMap("token", jwt);
return ResponseEntity.ok().body(body);
}
}
````
**请求api接口登录认证获取jwt**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3295df4eb54d4deb8498d3ae51aadcb8~tplv-k3u1fbpfcp-zoom-1.image)
**携带使用获取的jwt值请求api接口**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec0f95fe506946df8b14c8fab5ffd9f2~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fb6b8c67ec474e229ba37e62654651d5~tplv-k3u1fbpfcp-zoom-1.image)
##### 鉴权测试
通过上面的sureness.yml文件配置的用户-角色-资源,我们可以关联下面几个典型测试点
1. `/api/v1/source3===get`资源可以被任何直接访问,不需要认证鉴权
2. `api/v1/source2===get`资源持所有角色或无角色访问 前提是认证成功
3. 用户admin能访问`/api/v1/source1===get`资源,而用户root,tom无权限
4. 用户tom能访`/api/v1/source1===delete`资源而用户admin.root无权限
测试如下:
**`/api/v1/source3===get`资源可以被任何直接访问,不需要认证鉴权**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae2b3db8d7b64c9abf18c243943f7a4d~tplv-k3u1fbpfcp-zoom-1.image)
**`api/v1/source2===get`资源持所有角色或无角色访问 前提是认证成功**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ace184c86cc5438f91f7614b961dfab1~tplv-k3u1fbpfcp-zoom-1.image)
**用户admin能访问`/api/v1/source1===get`资源,而用户root,tom无权限**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f5fcd36befa84fb59b49c3cc35cb206e~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45f9454365ef4a45abc90e436eb0d2f0~tplv-k3u1fbpfcp-zoom-1.image)
**用户tom能访`/api/v1/source1===delete`资源而用户admin.root无权限**
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e9bce40c97dd4aaf9924845922c4917b~tplv-k3u1fbpfcp-zoom-1.image)
![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/209598b7717947c585bf479817b24726~tplv-k3u1fbpfcp-zoom-1.image)
### 其他
这次图文一步一步的详细描述了构建一个简单但完整的认证鉴权项目的流程,当然里面的授权账户等信息是写在配置文件里面的,实际的项目是会把这些数据写在数据库中。
万变不离其宗无论是写配置文件还是数据库它只是作为数据源提供数据基于sureness我们也能轻松快速构建基于数据库的认证鉴权项目支持动态刷新等各种功能。
基于数据库方案的项目可参考下方样例->sureness集成springboot样例(数据库方案), 此次一步一步完成的系统源代码也在下方 -> sureness集成springboot样例(配置文件方案)
<br>
#### DEMO源代码仓库
- [x] sureness集成springboot样例(配置文件方案) [sample-bootstrap](https://github.com/tomsun28/sureness/tree/master/sample-bootstrap)
- [x] sureness集成springboot样例(数据库方案) [sample-tom](https://github.com/tomsun28/sureness/tree/master/sample-tom)