Merge branch 'develop' of https://github.com/dibo-software/diboot-v2 into develop

This commit is contained in:
左春锐 2019-09-02 10:09:17 +08:00
commit cc5be7dd01
8 changed files with 304 additions and 47 deletions

View File

@ -37,7 +37,9 @@ module.exports = {
collapsable: true,
sidebarDepth: 2,
children: [
['/guide/diboot-shiro/安装', '安装'],
['/guide/diboot-shiro/权限设置', '权限设置'],
['/guide/diboot-shiro/权限缓存', '权限缓存'],
]
}
],
@ -61,7 +63,7 @@ module.exports = {
text: '学习',
items: [
{text: 'diboot-core指南', link: '/guide/diboot-core/安装'},
{text: 'diboot-shiro指南', link: '/guide/diboot-shiro/'},
{text: 'diboot-shiro指南', link: '/guide/diboot-shiro/安装'},
{text: 'diboot-devtools指南', link: '/guide/diboot-devtools/安装'}
]
}, {
@ -70,7 +72,7 @@ module.exports = {
{text: 'diboot-core', link: '/api/diboot-core/'}
]
},{
text: '1.x', link: 'https://diboot.com'
text: '1.x', link: 'https://www.diboot.com'
}, {
text: 'GitHub', link: 'https://github.com/dibo-software/diboot-v2'
}]

View File

@ -0,0 +1,47 @@
# 安装
## diboot-shiro是什么
> diboot-shiro是一款权限认证框架基于开源Shiro安全框架进行二次开发
* 采用当前流行的前后端分离架构基于jwt认证模式
* 默认提供`账号密码` 认证, 后续扩展组件包括:`企业微信`、`微信`等认证方式;
* 基于shiro细颗粒权限二次开发通过简单的配置系统中权限可自动写入数据库
* 基于shiro内存缓存系统操作权限后无须重启自动更新缓存权限为最新
## 引入依赖
> 目前diboot-shiro存放与diboot私有仓库使用时需要添加私有仓库地址
* Gradle项目
1. 添加仓库地址
在`build.gradle`的`repositories`配置项中添加仓库地址
```groovy
repositories {
maven{ url 'http://maven.diboot.com/repository/devtools/'}
}
```
2. 引入依赖
```groovy
compile ("com.diboot:diboot-shiro:2.0.1")
```
* Maven项目
1. 添加仓库地址
在`pom.xml`的`repositories`标签中添加仓库地址
```xml
<repositories>
<repository>
<id>diboot-devtools</id>
<url>http://maven.diboot.com/repository/devtools/</url>
</repository>
</repositories>
```
2. 引入依赖
```xml
<dependency>
<groupId>com.diboot</groupId>
<artifactId>diboot-shiro</artifactId>
<version>2.0.1</version>
</dependency>
```

View File

@ -0,0 +1,32 @@
# 权限缓存
> diboot-shiro的权限缓存基于shiro自带的内存缓存进行二次开发
### 缓存配置
```properties
#是否开启缓存默认false
diboot.shiro.cache.permission-caching-enabled=true
#缓存的方式memory
diboot.shiro.cache.cache-way=memory
```
::: tip
注:暂时只支持内存缓存redis已经纳入开发计划
:::
### 缓存注解
#### @AuthorizationCache
> 在资源授权校验过程中系统会频繁与数据库进行交互故而提供缓存机制。shiro的缓存仅仅是将登陆时候的将用户缓存记录下来
当用户更改了权限的时候,用户需要重新登陆权限才会生效。
#### 特点
> * 易开发:注解只需要加在操作权限的接口上
> * 操作简化:发生权限操作之后,将自动清空权限缓存,无须重新登陆
#### 示例
```java
@GetMapping("/list")
@AuthorizationCache
@AuthorizationWrapper(value = @RequiresPermissions("list"), name = "列表")
public JsonResult getList(){}
```

View File

@ -0,0 +1,125 @@
# 权限设置
> diboot-shiro是基于shiro注解@RequiresPermissions封装的细颗粒权限控制丰富了注解的内容更加方便权限的配置和管理。
## 权限配置
```properties
#是否存储数据库默认false 不开启
diboot.shiro.auth.storage=true
#存储环境{dev, test, prod}
# 默认dev环境不会删除代码中不存在的权限多人协作开发时需要开启否则回删除对方新增的权限
diboot.shiro.auth.env=dev
#配置所有权限均可访问的角色
diboot.shiro.auth.has-all-permissions-role-list[0]=ALL1
diboot.shiro.auth.has-all-permissions-role-list[1]=ALL2
```
> `diboot.shiro.auth.has-all-permissions-role-list`配置可访问任意接口的权限,如果不配置,默认是角色`ADMIN`
## 权限注解
### @AuthorizationPrefix
>- @AuthorizationPrefix@AuthorizationWrapper 需要搭配一起使用;
>- @AuthorizationPrefix 类注解,用于`标记类`、设置`权限前缀`等,作用当前类的所有方法;
### @AuthorizationWrapper
>- @AuthorizationWrapper 类/方法注解,包装@RequiresPermissions注解使用方式与之类似扩展了`权限名称`、`权限前缀`
>- @AuthorizationWrapper 方法上使用可以覆盖类上的权限配置
## 注解示例
#### 示例1
```java
/**
* 访问当前类的所有方法都需要具备`authorizationClass:global`权限
*/
@AuthorizationPrefix(name = "前缀", code = "authorizationClass", prefix = "authorizationClass")
@AuthorizationWrapper(value = @RequiresPermissions("global"), name = "全局权限")
public class AuthorizationWrapperClassController {
}
```
::: tip
`@AuthorizationPrefix`的`name`和`code`属性用于管理后台标记和展示,若开启数据库存储,那么将写入数据库;
`@AuthorizationPrefix`的`prefix`将作为当前类的权限前缀进行设置;
`@AuthorizationWrapper`的`value`对应`shiro`的`@RequiresPermissions`注解,将自动拼接前缀;
`@AuthorizationWrapper`的`name`标注当前权限的名称;
:::
#### 示例2
```java
/**
* test1方法覆盖了`authorizationClass:global`权限,需要`authorizationClass:test1`权限访问
*/
@AuthorizationPrefix(name = "前缀", code = "authorizationClass", prefix = "authorizationClass")
@AuthorizationWrapper(value = @RequiresPermissions("global"), name = "全局权限")
public class AuthorizationWrapperClassController {
@GetMapping("/test1")
@AuthorizationWrapper(value = @RequiresPermissions("test1"), name = "测试1")
public JsonResult test1() {}
}
```
#### 示例3
```java
/**
* test2方法的访问权限为`test`
*/
@AuthorizationPrefix(name = "前缀", code = "authorizationClass", prefix = "authorizationClass")
@AuthorizationWrapper(value = @RequiresPermissions("global"), name = "全局权限")
public class AuthorizationWrapperClassController {
@GetMapping("/test2")
@AuthorizationWrapper(value = @RequiresPermissions("test2"), name = "测试2", ignorePrefix = true)
public JsonResult test2() {}
}
```
::: tip
`@AuthorizationWrapper`的`ignorePrefix`表示是否忽略前缀,默认`false`,表示不忽略。
:::
#### 示例4
```java
/**
* test3方法访问权限为custom:test3
*/
@AuthorizationPrefix(name = "前缀", code = "authorizationClass", prefix = "authorizationClass")
@AuthorizationWrapper(value = @RequiresPermissions("global"), name = "全局权限")
public class AuthorizationWrapperClassController {
@GetMapping("/test3")
@AuthorizationWrapper(value = @RequiresPermissions("test3"), name = "测试3", prefix = "custom")
public JsonResult test3() {}
}
```
::: tip
`@AuthorizationWrapper`的`prefix`也表示前缀,优先级高于`@AuthorizationPrefix`的`prefix`,设置之后产生覆盖效果
:::
#### 示例5
```java
/**
* test3方法访问权限为custom:test3
*/
@AuthorizationPrefix(name = "前缀", code = "authorizationClass", prefix = "authorizationClass")
@AuthorizationWrapper(value = @RequiresPermissions("global"), name = "全局权限")
public class AuthorizationWrapperClassController {
@GetMapping("/test4or5")
@AuthorizationWrapper(value = @RequiresPermissions(value = {"test4", "test5"}, logical = Logical.AND),
name = {"测试4", "测试5"})
public JsonResult test4or5() {
return new JsonResult();
}
}
```
::: tip
`@AuthorizationWrapper`的name对应的是数组类型与`@RequiresPermissions#value`设置的权限为对应关系。
:::

View File

@ -0,0 +1,85 @@
package com.diboot.example.controller;
import com.diboot.core.util.JSON;
import com.diboot.core.vo.JsonResult;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 权限测试
* @author : wee
* @version : v 2.0
* @Date 2019-06-19 13:37
*/
@RestController
@RequestMapping("/authorizationClass")
@AuthorizationPrefix(name = "前缀", code = "authorizationClass", prefix = "authorizationClass")
@AuthorizationWrapper(value = @RequiresPermissions("global"), name = "全局权限")
public class AuthorizationWrapperClassController {
/**
* 全局权限authorizationClass:global
* @return
*/
@GetMapping("/test")
public JsonResult test() {
return new JsonResult();
}
/**
*
* 自定义权限1authorizationClass:test1
* @return
*/
@GetMapping("/test1")
@AuthorizationWrapper(value = @RequiresPermissions("test1"), name = "测试1")
public JsonResult test1() {
return new JsonResult();
}
/**
* 自定义权限2test2
* @return
*/
@GetMapping("/test2")
@AuthorizationWrapper(value = @RequiresPermissions("test2"), name = "测试2", ignorePrefix = true)
public JsonResult test2() {
return new JsonResult();
}
/**
* 自定义权限3custom:test3
* @return
*/
@GetMapping("/test3")
@AuthorizationWrapper(value = @RequiresPermissions("test3"), name = "测试3", prefix = "custom")
public JsonResult test3() {
return new JsonResult();
}
/**
* 自定义权限4同时满足 authorizationClass:test3 and authorizationClass:test4
* @return
*/
@GetMapping("/test4or5")
@AuthorizationWrapper(value = @RequiresPermissions(value = {"test4", "test5"}, logical = Logical.AND),
name = {"测试4", "测试5"})
public JsonResult test4or5() {
return new JsonResult();
}
/**
* @return
*/
@GetMapping("/test6")
@AuthorizationWrapper(value = @RequiresPermissions(value = "test7"),
name = "测试7")
public JsonResult test6() {
return new JsonResult();
}
}

View File

@ -32,9 +32,6 @@ import java.util.stream.Collectors;
* {@link AuthorizationStorage}中封装了将{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}权限自动入库的操作,
* <strong>权限入库每个Controller需要加上类注解{@link AuthorizationPrefix}用于识别</strong>
* <br/>
* 当你使用注解{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}, 且需要自动让权限入库请实现{@link ApplicationListener}并注入该类
* <br/>
* 调用类中方法{@link AuthorizationStorage#autoStorage(ApplicationContext)}
* @author : wee
* @version : v2.0
* @Date 2019-06-27 10:01

View File

@ -1,29 +1,23 @@
package com.diboot.example.listener;
package com.diboot.shiro.authz.storage;
import com.diboot.core.util.V;
import com.diboot.shiro.authz.storage.AuthorizationStorage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 示例监听器,权限入库
* 权限入库
* @author : wee
* @version : v 2.0
* @Date 2019-06-18 23:11
* @version : v1.0
* @Date 2019-08-28 10:44
*/
@Component
public class ExampleListener implements ApplicationListener<ContextRefreshedEvent> {
public class StorageListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private AuthorizationStorage authorizationStorage;
/**
* Handle an application event.
*
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (V.isEmpty(event.getApplicationContext().getParent())) {

View File

@ -1,48 +1,23 @@
package com.diboot.shiro.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
/***
* 认证方式
* @author Yangzhao
* @version v2.0
* @date 2019/6/6
*/
@Getter
@AllArgsConstructor
public enum AuthType {
USERNAME_PASSWORD(1, true, "账号密码"),
WX_MP(2, false, "公众号"),
WX_CP(3, false, "企业微信");
private AuthType(int code, boolean requirePassword, String label){
this.code = code;
this.requirePassword = requirePassword;
this.label = label;
}
private int code;
private boolean requirePassword;
private String label;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public boolean isRequirePassword() {
return requirePassword;
}
public void setRequirePassword(boolean requirePassword) {
this.requirePassword = requirePassword;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}