Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	diboot-example/src/main/java/com/diboot/example/controller/OrganizationController.java
#	diboot-example/src/main/java/com/diboot/example/controller/SysUserController.java
#	diboot-shiro/src/main/java/com/diboot/shiro/controller/RoleController.java
This commit is contained in:
mazhicheng 2019-08-07 09:50:54 +08:00
commit 9fdeda639d
25 changed files with 505 additions and 61 deletions

View File

@ -35,6 +35,11 @@ RBAC的角色权限+基于Shiro的细粒度权限控制
#### 2、@AuthorizationWrapper #### 2、@AuthorizationWrapper
类/方法注解在保证shiro的@RequirePermissions注解的功能基础上增加名称、权限前缀特性使用方式同@RequiresPermissions 类/方法注解在保证shiro的@RequirePermissions注解的功能基础上增加名称、权限前缀特性使用方式同@RequiresPermissions
#### 3、@AuthorizationCache
方法注解,在资源授权校验过程中,系统会频繁与数据库进行交互,故而提供缓存机制
* 缓存时机:缓存会在用户第一次进行权限验证的之后缓存数据
* 当前注解作用:如果通过系统调整权限,只需要将该注解加在更新或添加权限处,将会清空权限缓存,下次进入将重新加载权限
#### 3、AuthorizationProperties #### 3、AuthorizationProperties
提供一些权限相关的配置,主要包括: 提供一些权限相关的配置,主要包括:
- 权限环境变量提供dev、test、prod三种选项 - 权限环境变量提供dev、test、prod三种选项
@ -54,6 +59,11 @@ diboot.shiro.auth.env=dev
diboot.shiro.auth.has-all-permissions-role-list[0]=ALL1 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[1]=ALL2
diboot.shiro.auth.has-all-permissions-role-list[2]=ALL3 diboot.shiro.auth.has-all-permissions-role-list[2]=ALL3
#配置权限缓存机制
##是否开启缓存
diboot.shiro.cache.permission-caching-enabled=true
##缓存方式暂时提供shiro内置的内存缓存
diboot.shiro.cache.cache-way=memory
``` ```
#### 4、AuthorizationStorage #### 4、AuthorizationStorage

View File

@ -163,8 +163,8 @@ public class DepartmentController extends BaseCrudRestController {
/* /*
* 根据组织ID获取部门kv list * 根据组织ID获取部门kv list
* */ * */
@GetMapping("/getDepartment/{orgId}") @GetMapping("/getDepartmentKV/{orgId}")
public JsonResult getDepartment(@PathVariable Long orgId, HttpServletRequest request){ public JsonResult getDepartmentKV(@PathVariable Long orgId, HttpServletRequest request){
Wrapper wrapper = null; Wrapper wrapper = null;
//获取部门KV //获取部门KV
wrapper = new QueryWrapper<Department>() wrapper = new QueryWrapper<Department>()
@ -176,6 +176,21 @@ public class DepartmentController extends BaseCrudRestController {
return new JsonResult(deptKvList); return new JsonResult(deptKvList);
} }
/*
* 根据组织ID获取部门list
* */
@GetMapping("/getDepartmentList/{orgId}")
public JsonResult getDepartmentList(@PathVariable Long orgId, HttpServletRequest request) throws Exception {
// 构建分页
Pagination pagination = buildPagination(request);
Wrapper wrapper = new QueryWrapper<Department>()
.lambda()
.eq(Department::getOrgId, orgId);
List<DepartmentVO> voList = departmentService.getViewObjectList(wrapper, pagination, DepartmentVO.class);
return new JsonResult(voList);
}
@Override @Override
protected BaseService getService() { protected BaseService getService() {
return departmentService; return departmentService;

View File

@ -6,6 +6,7 @@ import com.diboot.core.binding.RelationsBinder;
import com.diboot.core.controller.BaseCrudRestController; import com.diboot.core.controller.BaseCrudRestController;
import com.diboot.core.service.BaseService; import com.diboot.core.service.BaseService;
import com.diboot.core.service.DictionaryService; import com.diboot.core.service.DictionaryService;
import com.diboot.core.util.V;
import com.diboot.core.vo.JsonResult; import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.KeyValue; import com.diboot.core.vo.KeyValue;
import com.diboot.core.vo.Pagination; import com.diboot.core.vo.Pagination;
@ -36,12 +37,13 @@ public class OrganizationController extends BaseCrudRestController {
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
@GetMapping("/list") @GetMapping("/list")
public JsonResult getVOList(Organization organization, Pagination pagination, HttpServletRequest request) throws Exception{ public JsonResult getVOList(HttpServletRequest request) throws Exception{
QueryWrapper<Organization> queryWrapper = super.buildQueryWrapper(organization); QueryWrapper<Organization> queryWrapper = buildQuery(request);
queryWrapper.lambda().eq(Organization::getParentId, 0);
// 构建分页
Pagination pagination = buildPagination(request);
// 查询当前页的Entity主表数据 // 查询当前页的Entity主表数据
List<Organization> entityList = organizationService.getEntityList(queryWrapper, pagination); List<OrganizationVO> voList = organizationService.getOrganizatioList(queryWrapper, pagination);
//筛选出在列表页展示的字段
List<OrganizationVO> voList = RelationsBinder.convertAndBind(entityList, OrganizationVO.class);
// 返回结果 // 返回结果
return new JsonResult(Status.OK, voList).bindPagination(pagination); return new JsonResult(Status.OK, voList).bindPagination(pagination);
} }
@ -83,10 +85,11 @@ public class OrganizationController extends BaseCrudRestController {
@GetMapping("/attachMore") @GetMapping("/attachMore")
public JsonResult attachMore(HttpServletRequest request, ModelMap modelMap){ public JsonResult attachMore(HttpServletRequest request, ModelMap modelMap){
Wrapper wrapper = null; Wrapper wrapper = null;
//获取组织机构KV //获取组织机构KV
wrapper = new QueryWrapper<Organization>() wrapper = new QueryWrapper<Organization>()
.lambda() .lambda()
.select(Organization::getName, Organization::getId); .select(Organization::getName, Organization::getId)
.eq(Organization::getParentId, 0);
List<KeyValue> orgKvList = organizationService.getKeyValueList(wrapper); List<KeyValue> orgKvList = organizationService.getKeyValueList(wrapper);
modelMap.put("orgKvList", orgKvList); modelMap.put("orgKvList", orgKvList);
@ -97,6 +100,25 @@ public class OrganizationController extends BaseCrudRestController {
return new JsonResult(modelMap); return new JsonResult(modelMap);
} }
@GetMapping("/getOrgTree")
public JsonResult getOrgTree() throws Exception{
QueryWrapper<Organization> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(Organization::getParentId, 0);
List<Organization> orgList = organizationService.getEntityList(queryWrapper);
List<OrganizationVO> voList = RelationsBinder.convertAndBind(orgList, OrganizationVO.class);
if(V.notEmpty(voList)){
for(OrganizationVO vo : voList){
queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Organization::getParentId, vo.getId());
List<Organization> childList = organizationService.getEntityList(queryWrapper);
List<OrganizationVO> childvVoList = RelationsBinder.convertAndBind(childList, OrganizationVO.class);
vo.setChildren(childvVoList);
}
}
return new JsonResult(orgList);
}
@Override @Override
protected BaseService getService() { protected BaseService getService() {
return organizationService; return organizationService;

View File

@ -114,8 +114,8 @@ public class PositionController extends BaseCrudRestController {
/* /*
* 根据部门ID获取职位kv list * 根据部门ID获取职位kv list
* */ * */
@GetMapping("/getPosition/{deptId}") @GetMapping("/getPositionKV/{deptId}")
public JsonResult getPosition(@PathVariable Long deptId, HttpServletRequest request){ public JsonResult getPositionKV(@PathVariable Long deptId, HttpServletRequest request){
Wrapper wrapper = null; Wrapper wrapper = null;
List<Long> positionIdList = new ArrayList<>(); List<Long> positionIdList = new ArrayList<>();
wrapper = new LambdaQueryWrapper<PositionDepartment>() wrapper = new LambdaQueryWrapper<PositionDepartment>()

View File

@ -18,10 +18,15 @@ import com.diboot.example.service.DepartmentService;
import com.diboot.example.service.SysUserService; import com.diboot.example.service.SysUserService;
import com.diboot.example.vo.SysUserListVO; import com.diboot.example.vo.SysUserListVO;
import com.diboot.example.vo.SysUserVO; import com.diboot.example.vo.SysUserVO;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.entity.Role; import com.diboot.shiro.entity.Role;
import com.diboot.shiro.service.PermissionService;
import com.diboot.shiro.service.RoleService; import com.diboot.shiro.service.RoleService;
import com.diboot.shiro.util.JwtHelper; import com.diboot.shiro.util.JwtHelper;
import com.diboot.shiro.vo.RoleVO; import com.diboot.shiro.vo.RoleVO;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -34,6 +39,7 @@ import java.util.List;
@RestController @RestController
@RequestMapping("/sysUser") @RequestMapping("/sysUser")
@AuthorizationPrefix(name = "用户管理", code = "sysUser", prefix = "sysUser")
public class SysUserController extends BaseCrudRestController { public class SysUserController extends BaseCrudRestController {
private static final Logger logger = LoggerFactory.getLogger(SysUserController.class); private static final Logger logger = LoggerFactory.getLogger(SysUserController.class);
@ -47,12 +53,19 @@ public class SysUserController extends BaseCrudRestController {
@Autowired @Autowired
private RoleService roleService; private RoleService roleService;
@Autowired
private PermissionService permissionService;
@Autowired @Autowired
private DepartmentService departmentService; private DepartmentService departmentService;
@GetMapping("/list") @GetMapping("/list")
public JsonResult getVOList(SysUser sysUser, Pagination pagination, HttpServletRequest request) throws Exception{ @AuthorizationWrapper(value = @RequiresPermissions("list"), name = "列表")
QueryWrapper<SysUser> queryWrapper = super.buildQueryWrapper(sysUser); public JsonResult getVOList(HttpServletRequest request) throws Exception{
QueryWrapper<SysUser> queryWrapper = buildQuery(request);
// 构建分页
Pagination pagination = buildPagination(request);
// 查询当前页的Entity主表数据 // 查询当前页的Entity主表数据
List<SysUserVO> voList = sysUserService.getSysUserList(queryWrapper, pagination); List<SysUserVO> voList = sysUserService.getSysUserList(queryWrapper, pagination);
//筛选出在列表页展示的字段 //筛选出在列表页展示的字段
@ -67,6 +80,7 @@ public class SysUserController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@PostMapping("/") @PostMapping("/")
@AuthorizationWrapper(value = @RequiresPermissions("create"), name = "新建")
public JsonResult createEntity(@RequestBody SysUser entity, BindingResult result, HttpServletRequest request) public JsonResult createEntity(@RequestBody SysUser entity, BindingResult result, HttpServletRequest request)
throws Exception{ throws Exception{
boolean success = sysUserService.createSysUser(entity); boolean success = sysUserService.createSysUser(entity);
@ -85,6 +99,7 @@ public class SysUserController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@PutMapping("/{id}") @PutMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("update"), name = "更新")
public JsonResult updateModel(@PathVariable("id")Long id, @RequestBody SysUser entity, BindingResult result, public JsonResult updateModel(@PathVariable("id")Long id, @RequestBody SysUser entity, BindingResult result,
HttpServletRequest request) throws Exception{ HttpServletRequest request) throws Exception{
// Model属性值验证结果 // Model属性值验证结果
@ -107,6 +122,7 @@ public class SysUserController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@GetMapping("/{id}") @GetMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("read"), name = "读取")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request) public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{ throws Exception{
SysUserVO sysUserVO = sysUserService.getSysUser(id); SysUserVO sysUserVO = sysUserService.getSysUser(id);
@ -120,6 +136,7 @@ public class SysUserController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("delete"), name = "删除")
public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{ public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{
boolean success = sysUserService.deleteSysUser(id); boolean success = sysUserService.deleteSysUser(id);
if(success){ if(success){
@ -215,7 +232,16 @@ public class SysUserController extends BaseCrudRestController {
List<RoleVO> roleVOList = roleService.getRelatedRoleAndPermissionListByUser(SysUser.class.getSimpleName(), user.getId()); List<RoleVO> roleVOList = roleService.getRelatedRoleAndPermissionListByUser(SysUser.class.getSimpleName(), user.getId());
if (V.isEmpty(roleVOList)){ if (V.isEmpty(roleVOList)){
return new JsonResult(Status.FAIL_OPERATION, new String[]{"获取用户角色失败"}); return new JsonResult(Status.FAIL_OPERATION, new String[]{"用户未配置角色,获取数据失败"});
}
// 如果具有管理员角色则赋予所有权限
for (RoleVO roleVO : roleVOList){
if (roleVO.isAdmin()){
List<Permission> allPermissionList = permissionService.getEntityList(null);
roleVO.setPermissionList(allPermissionList);
break;
}
} }
user.setRoleVOList(roleVOList); user.setRoleVOList(roleVOList);

View File

@ -1,7 +1,12 @@
package com.diboot.example.service; package com.diboot.example.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.diboot.core.service.BaseService; import com.diboot.core.service.BaseService;
import com.diboot.core.vo.Pagination;
import com.diboot.example.entity.Organization; import com.diboot.example.entity.Organization;
import com.diboot.example.vo.OrganizationVO;
import java.util.List;
/** /**
* 单位相关Service * 单位相关Service
@ -11,4 +16,6 @@ import com.diboot.example.entity.Organization;
*/ */
public interface OrganizationService extends BaseService<Organization> { public interface OrganizationService extends BaseService<Organization> {
List<OrganizationVO> getOrganizatioList(Wrapper wrapper, Pagination pagination);
} }

View File

@ -1,12 +1,20 @@
package com.diboot.example.service.impl; package com.diboot.example.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.diboot.core.binding.RelationsBinder;
import com.diboot.core.service.impl.BaseServiceImpl; import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.V;
import com.diboot.core.vo.Pagination;
import com.diboot.example.entity.Organization; import com.diboot.example.entity.Organization;
import com.diboot.example.mapper.OrganizationMapper; import com.diboot.example.mapper.OrganizationMapper;
import com.diboot.example.service.OrganizationService; import com.diboot.example.service.OrganizationService;
import com.diboot.example.vo.OrganizationVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
/** /**
* 单位相关Service实现 * 单位相关Service实现
* @author Mazhicheng * @author Mazhicheng
@ -17,4 +25,17 @@ import org.springframework.stereotype.Service;
@Slf4j @Slf4j
public class OrganizationServiceImpl extends BaseServiceImpl<OrganizationMapper, Organization> implements OrganizationService { public class OrganizationServiceImpl extends BaseServiceImpl<OrganizationMapper, Organization> implements OrganizationService {
@Override
public List<OrganizationVO> getOrganizatioList(Wrapper wrapper, Pagination pagination) {
List<OrganizationVO> voList = super.getViewObjectList(wrapper, pagination, OrganizationVO.class);
if(V.notEmpty(voList)){
for(OrganizationVO vo : voList){
wrapper = new LambdaQueryWrapper<Organization>().eq(Organization::getParentId, vo.getId());
List<Organization> orgList = super.getEntityList(wrapper);
List<OrganizationVO> orgVoList = RelationsBinder.convertAndBind(orgList, OrganizationVO.class);
vo.setChildren(orgVoList);
}
}
return voList;
}
} }

View File

@ -5,6 +5,8 @@ import com.diboot.core.binding.annotation.BindField;
import com.diboot.example.entity.Organization; import com.diboot.example.entity.Organization;
import lombok.Data; import lombok.Data;
import java.util.List;
/** /**
* @author wangyongliang * @author wangyongliang
* @version v2.0 * @version v2.0
@ -22,4 +24,6 @@ public class OrganizationVO extends Organization {
@BindDict(type = "INDUSTRY", field = "industry") @BindDict(type = "INDUSTRY", field = "industry")
private String industryLabel; private String industryLabel;
private List<OrganizationVO> children;
} }

View File

@ -98,6 +98,10 @@ 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[1]=ALL2
diboot.shiro.auth.has-all-permissions-role-list[2]=ALL3 diboot.shiro.auth.has-all-permissions-role-list[2]=ALL3
#权限缓存机制
diboot.shiro.cache.permission-caching-enabled=true
diboot.shiro.cache.cache-way=memory
#------web页面访问的时候需要如下配置---- #------web页面访问的时候需要如下配置----
spring.mvc.view.prefix=/static spring.mvc.view.prefix=/static
spring.mvc.view.suffix=.html spring.mvc.view.suffix=.html

View File

@ -5,6 +5,7 @@ dependencies {
// compile("org.springframework.boot:spring-boot-configuration-processor") // compile("org.springframework.boot:spring-boot-configuration-processor")
compile("org.apache.shiro:shiro-spring:1.4.1") compile("org.apache.shiro:shiro-spring:1.4.1")
compile("org.aspectj:aspectjweaver")
compile("com.auth0:java-jwt:3.4.1", compile("com.auth0:java-jwt:3.4.1",
"io.jsonwebtoken:jjwt:0.9.1") "io.jsonwebtoken:jjwt:0.9.1")

View File

@ -0,0 +1,20 @@
package com.diboot.shiro.authz.annotation;
import java.lang.annotation.*;
/**
* 权限缓存
* <p>
* 缓存目的在资源授权校验过程中系统会频繁与数据库进行交互故而提供缓存机制<br/>
* 缓存时机缓存会在用户第一次进行权限验证的之后缓存数据
* 当前注解作用如果通过系统调整角色的权限只需要将该注解加在更新或添加权限处将会清空缓存下次进入将重新加载
* </p>
* @author : wee
* @version v1.0
* @Date 2019-07-23 09:27
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AuthorizationCache {
}

View File

@ -0,0 +1,57 @@
package com.diboot.shiro.authz.aspect;
import com.diboot.core.util.V;
import com.diboot.shiro.authz.annotation.AuthorizationCache;
import com.diboot.shiro.jwt.BaseJwtRealm;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.tomcat.util.http.parser.Authorization;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 当有操作的时候自动更新被注解的相关数据
* @author : wee
* @version : v2.0
* @Date 2019-07-24 23:20
*/
@Slf4j
@Aspect
@Component
public class CacheHandler{
private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache";
@Autowired
private CacheManager cacheManager;
/**
* 设置切片
*/
@Pointcut("@annotation(com.diboot.shiro.authz.annotation.AuthorizationCache)")
public void proxyAspect() {}
/**
* 当请求{@link AuthorizationCache}注解的方法执行完成后自动触发此处切面
* 作用重新缓存权限和方法
* @param joinPoint
*/
@AfterReturning("proxyAspect()")
public void afterReturning(JoinPoint joinPoint) {
try {
log.info("【修改权限】==> 正在调用【{}#{}()】方法修改!", joinPoint.getThis().getClass(), joinPoint.getSignature().getName());
Cache<Object, Authorization> cache = cacheManager.getCache(BaseJwtRealm.class.getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX);
//统一删除所有的缓存所有用户需重新加载缓存
if (V.notEmpty(cache) && cache.size() > 0) {
cache.clear();
}
} catch (Exception e) {
log.info("【修改权限】==> 调用【{}#{}()】异常:", joinPoint.getThis().getClass(), joinPoint.getSignature().getName(), e);
}
}
}

View File

@ -0,0 +1,26 @@
package com.diboot.shiro.authz.cache;
import com.diboot.shiro.authz.properties.AuthCacheProperties;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
*
* memory条件类用与创建memory缓存对象
* @author : wee
* @version : v2.0
* @Date 2019-08-05 14:39
*/
public class MemoryCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取配置信息
Boolean enableCached = context.getEnvironment().getProperty(AuthCacheProperties.CACHE_PREFIX + ".permission-caching-enabled", Boolean.class);
enableCached = enableCached == null ? false : enableCached;
AuthCacheProperties.CacheWay cacheWay = context.getEnvironment().getProperty(AuthCacheProperties.CACHE_PREFIX + ".cache-way", AuthCacheProperties.CacheWay.class);
cacheWay = cacheWay == null ? AuthCacheProperties.CacheWay.MEMORY : cacheWay;
return enableCached && AuthCacheProperties.CacheWay.MEMORY.equals(cacheWay);
}
}

View File

@ -0,0 +1,51 @@
package com.diboot.shiro.authz.cache;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import java.util.Collection;
import java.util.Set;
/**
* TODO redis缓存处理等候完善
* @author : wee
* @version : v2.0
* @Date 2019-08-05 16:20
*/
public class RedisCache<K, V> implements Cache<K, V> {
@Override
public V get(K k) throws CacheException {
return null;
}
@Override
public V put(K k, V v) throws CacheException {
return null;
}
@Override
public V remove(K k) throws CacheException {
return null;
}
@Override
public void clear() throws CacheException {
//TODO 根据模糊key获取redis中所有的权限然后统一清空
}
@Override
public int size() {
//TODO 模糊key获取redis所有当前系统中的权限
return 0;
}
@Override
public Set<K> keys() {
return null;
}
@Override
public Collection<V> values() {
return null;
}
}

View File

@ -0,0 +1,28 @@
package com.diboot.shiro.authz.cache;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;
/**
* TODO redis缓存管理暂时不提供
* @author : wee
* @version : v2.0
* @Date 2019-08-05 16:15
*/
@Slf4j
public class RedisCacheManager implements CacheManager, Destroyable {
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
log.error("【缓存】<== 尚未实现redis缓存暂时不可用请选择内存缓存");
return null;
}
@Override
public void destroy() throws Exception {
//清除redis内容
}
}

View File

@ -0,0 +1,24 @@
package com.diboot.shiro.authz.cache;
import com.diboot.shiro.authz.properties.AuthCacheProperties;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* redis条件类用与创建redis缓存对象
* @author : wee
* @version : v2.0
* @Date 2019-08-05 14:35
*/
public class RedisCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取配置信息
Boolean enableCached = context.getEnvironment().getProperty(AuthCacheProperties.CACHE_PREFIX + ".permission-caching-enabled", Boolean.class);
enableCached = enableCached == null ? false : enableCached;
AuthCacheProperties.CacheWay cacheWay = context.getEnvironment().getProperty(AuthCacheProperties.CACHE_PREFIX + ".cache-way", AuthCacheProperties.CacheWay.class);
return enableCached && AuthCacheProperties.CacheWay.REDIS.equals(cacheWay);
}
}

View File

@ -1,12 +1,12 @@
package com.diboot.shiro.authz.config; package com.diboot.shiro.authz.config;
import com.diboot.shiro.authz.properties.AuthorizationProperties; import com.diboot.shiro.authz.properties.AuthorizationProperties;
import com.diboot.shiro.authz.properties.AuthCacheProperties;
import com.diboot.shiro.authz.storage.AuthorizationStorage; import com.diboot.shiro.authz.storage.AuthorizationStorage;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/** /**
* 权限配置 * 权限配置
@ -15,7 +15,7 @@ import org.springframework.core.annotation.Order;
* @Date 2019-06-27 10:30 * @Date 2019-06-27 10:30
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(AuthorizationProperties.class) @EnableConfigurationProperties({AuthorizationProperties.class, AuthCacheProperties.class})
public class AuthorizationAutoConfiguration { public class AuthorizationAutoConfiguration {
@Autowired @Autowired

View File

@ -0,0 +1,50 @@
package com.diboot.shiro.authz.properties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author : wee
* @version : v2.0
* @Date 2019-07-29 15:59
*/
@Data
@ConfigurationProperties(AuthCacheProperties.CACHE_PREFIX)
public class AuthCacheProperties {
public final static String CACHE_PREFIX = "diboot.shiro.cache";
/**
* 是否开启权限缓存默认false
*/
private boolean permissionCachingEnabled = false;
/**
* 缓存方式默认内存缓存
*/
private CacheWay cacheWay = CacheWay.MEMORY;
/**
* 缓存方式
* <p>
* 当前提供本地缓存
* </p>
*/
@Getter
@AllArgsConstructor
public enum CacheWay {
/**
* 内存缓存
*/
MEMORY,
/**
* redis缓存 TODO 尚未实现暂不可用
*/
@Deprecated
REDIS;
}
}

View File

@ -4,13 +4,12 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.Order;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 权限入库配置文件 * 权限入库配置文件
*
* @author : wee * @author : wee
* @version : v2.0 * @version : v2.0
* @Date 2019-06-27 10:16 * @Date 2019-06-27 10:16
@ -19,26 +18,38 @@ import java.util.List;
@ConfigurationProperties(prefix = "diboot.shiro.auth") @ConfigurationProperties(prefix = "diboot.shiro.auth")
public class AuthorizationProperties { public class AuthorizationProperties {
/**设置权限存储的环境:其中开发环境权限不会替换删除,测试和生产会替换删除*/ /**
* 设置权限存储的环境其中开发环境权限不会替换删除测试和生产会替换删除
*/
private EnvEnum env = EnvEnum.DEV; private EnvEnum env = EnvEnum.DEV;
/**是否开启存储权限*/ /**
* 是否开启存储权限
*/
private boolean storage = false; private boolean storage = false;
/**具有所有权限的角色*/ /**
* 具有所有权限的角色
*/
private List<String> hasAllPermissionsRoleList; private List<String> hasAllPermissionsRoleList;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum EnvEnum { public enum EnvEnum {
/**生产环境*/ /**
* 生产环境
*/
PROD("prod"), PROD("prod"),
/**测试环境*/ /**
* 测试环境
*/
TEST("test"), TEST("test"),
/**开发环境*/ /**
* 开发环境
*/
DEV("dev"); DEV("dev");
private String env; private String env;

View File

@ -1,9 +1,16 @@
package com.diboot.shiro.config; package com.diboot.shiro.config;
import com.diboot.core.util.V;
import com.diboot.shiro.authz.aop.CustomAuthorizationAttributeSourceAdvisor; import com.diboot.shiro.authz.aop.CustomAuthorizationAttributeSourceAdvisor;
import com.diboot.shiro.authz.cache.MemoryCondition;
import com.diboot.shiro.authz.cache.RedisCacheManager;
import com.diboot.shiro.authz.cache.RedisCondition;
import com.diboot.shiro.authz.properties.AuthorizationProperties; import com.diboot.shiro.authz.properties.AuthorizationProperties;
import com.diboot.shiro.authz.properties.AuthCacheProperties;
import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter; import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter;
import com.diboot.shiro.jwt.BaseJwtRealm; import com.diboot.shiro.jwt.BaseJwtRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm; import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.LifecycleBeanPostProcessor;
@ -14,8 +21,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
@ -30,16 +39,47 @@ import java.util.Map;
* @date 2019/6/6 * @date 2019/6/6
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(AuthorizationProperties.class) @AutoConfigureAfter(AuthCacheProperties.class)
@EnableConfigurationProperties({AuthorizationProperties.class, AuthCacheProperties.class})
public class ShiroConfig { public class ShiroConfig {
private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class); private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
@Autowired @Autowired
private AuthorizationProperties authorizationProperties; private AuthorizationProperties authorizationProperties;
@Autowired
private AuthCacheProperties authCacheProperties;
/**
* 将数据缓存到内存中
* @return
*/
@Bean("cacheManager")
@Conditional(MemoryCondition.class)
public CacheManager memoryCacheManager() {
return new MemoryConstrainedCacheManager();
}
/**
* 将数据存储到redis缓存
* @return
*/
@Bean("cacheManager")
@Conditional(RedisCondition.class)
public CacheManager redisCacheManager() {
return new RedisCacheManager();
}
@Bean @Bean
public Realm realm(){ public Realm realm(){
BaseJwtRealm realm = new BaseJwtRealm(); BaseJwtRealm realm = new BaseJwtRealm();
if (authCacheProperties.isPermissionCachingEnabled()) {
//设置权限缓存
realm.setCachingEnabled(true);
CacheManager cacheManager = V.notEmpty(redisCacheManager())? redisCacheManager(): memoryCacheManager();
realm.setCacheManager(cacheManager);
}
return realm; return realm;
} }

View File

@ -30,8 +30,8 @@ import java.util.List;
* @date 2019/6/20 * @date 2019/6/20
*/ */
@RestController @RestController
@AuthorizationPrefix(prefix = "permission", code = "permission", name = "权限")
@RequestMapping("/permission") @RequestMapping("/permission")
@AuthorizationPrefix(prefix = "permission", code = "permission", name = "权限管理")
public class PermissionController extends BaseCrudRestController { public class PermissionController extends BaseCrudRestController {
private static final Logger logger = LoggerFactory.getLogger(PermissionService.class); private static final Logger logger = LoggerFactory.getLogger(PermissionService.class);
@ -46,7 +46,7 @@ public class PermissionController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@GetMapping("/{id}") @GetMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("get"), name = "查看") @AuthorizationWrapper(value = @RequiresPermissions("read"), name = "读取")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request) public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{ throws Exception{
PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class); PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class);
@ -76,8 +76,8 @@ public class PermissionController extends BaseCrudRestController {
* @return * @return
* @throws Exception * @throws Exception
*/ */
@RequiresPermissions("permission:add")
@PostMapping("/") @PostMapping("/")
// @AuthorizationWrapper(value = @RequiresPermissions("create"), name = "新建")
public JsonResult createEntity(@ModelAttribute PermissionVO viewObject, BindingResult result, HttpServletRequest request) public JsonResult createEntity(@ModelAttribute PermissionVO viewObject, BindingResult result, HttpServletRequest request)
throws Exception{ throws Exception{
// 转换 // 转换
@ -92,8 +92,8 @@ public class PermissionController extends BaseCrudRestController {
* @return * @return
* @throws Exception * @throws Exception
*/ */
@RequiresPermissions("permission:update")
@PutMapping("/{id}") @PutMapping("/{id}")
// @AuthorizationWrapper(value = @RequiresPermissions("update"), name = "更新")
public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute Permission entity, BindingResult result, public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute Permission entity, BindingResult result,
HttpServletRequest request) throws Exception{ HttpServletRequest request) throws Exception{
return super.updateEntity(entity, result); return super.updateEntity(entity, result);
@ -105,8 +105,8 @@ public class PermissionController extends BaseCrudRestController {
* @return * @return
* @throws Exception * @throws Exception
*/ */
@RequiresPermissions("permission:delete")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
// @AuthorizationWrapper(value = @RequiresPermissions("delete"), name = "删除")
public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{ public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{
return super.deleteEntity(id); return super.deleteEntity(id);
} }

View File

@ -10,10 +10,14 @@ import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.KeyValue; import com.diboot.core.vo.KeyValue;
import com.diboot.core.vo.Pagination; import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status; import com.diboot.core.vo.Status;
import com.diboot.shiro.authz.annotation.AuthorizationCache;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import com.diboot.shiro.entity.Permission; import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.entity.Role; import com.diboot.shiro.entity.Role;
import com.diboot.shiro.service.RoleService; import com.diboot.shiro.service.RoleService;
import com.diboot.shiro.vo.RoleVO; import com.diboot.shiro.vo.RoleVO;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
@ -24,6 +28,7 @@ import java.util.List;
@RestController @RestController
@RequestMapping("/role") @RequestMapping("/role")
@AuthorizationPrefix(prefix = "role", code = "role", name = "角色管理")
public class RoleController extends BaseCrudRestController { public class RoleController extends BaseCrudRestController {
@Autowired @Autowired
@ -43,33 +48,25 @@ public class RoleController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@GetMapping("/list") @GetMapping("/list")
public JsonResult getVOList(Role role, Pagination pagination, HttpServletRequest request) throws Exception{ @AuthorizationWrapper(value = @RequiresPermissions("list"), name = "列表")
QueryWrapper<Role> queryWrapper = super.buildQueryWrapper(role); @AuthorizationCache
public JsonResult getVOList(HttpServletRequest request) throws Exception{
QueryWrapper<Role> queryWrapper = buildQuery(request);
// 构建分页
Pagination pagination = buildPagination(request);
// 获取结果 // 获取结果
List<RoleVO> voList = roleService.getRoleList(queryWrapper, pagination); List<RoleVO> voList = roleService.getRoleList(queryWrapper, pagination);
// 返回结果 // 返回结果
return new JsonResult(Status.OK, voList).bindPagination(pagination); return new JsonResult(Status.OK, voList).bindPagination(pagination);
} }
/***
* 显示创建页面
* @return
* @throws Exception
*/
@GetMapping("/toCreatePage")
public JsonResult toCreatePage(HttpServletRequest request, ModelMap modelMap)
throws Exception{
List<Permission> menuList = roleService.getAllMenu();
modelMap.put("menuList", menuList);
return new JsonResult(modelMap);
}
/*** /***
* 创建Entity * 创建Entity
* @return * @return
* @throws Exception * @throws Exception
*/ */
@PostMapping("/") @PostMapping("/")
@AuthorizationWrapper(value = @RequiresPermissions("create"), name = "新建")
public JsonResult createEntity(@RequestBody Role entity, BindingResult result, HttpServletRequest request) public JsonResult createEntity(@RequestBody Role entity, BindingResult result, HttpServletRequest request)
throws Exception{ throws Exception{
// 创建 // 创建
@ -81,19 +78,6 @@ public class RoleController extends BaseCrudRestController {
} }
} }
/***
* 显示更新页面
* @return
* @throws Exception
*/
@GetMapping("/toUpdatePage/{id}")
public JsonResult toUpdatePage(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{
RoleVO roleVO = roleService.toUpdatePage(id);
return new JsonResult(roleVO);
}
/*** /***
* 更新Entity * 更新Entity
* @param id ID * @param id ID
@ -101,6 +85,7 @@ public class RoleController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@PutMapping("/{id}") @PutMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("update"), name = "更新")
public JsonResult updateModel(@PathVariable("id")Long id, @RequestBody Role entity, BindingResult result, public JsonResult updateModel(@PathVariable("id")Long id, @RequestBody Role entity, BindingResult result,
HttpServletRequest request) throws Exception{ HttpServletRequest request) throws Exception{
// Model属性值验证结果 // Model属性值验证结果
@ -125,6 +110,7 @@ public class RoleController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@GetMapping("/{id}") @GetMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("read"), name = "读取")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request) public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{ throws Exception{
RoleVO roleVO = roleService.getRole(id); RoleVO roleVO = roleService.getRole(id);
@ -138,6 +124,7 @@ public class RoleController extends BaseCrudRestController {
* @throws Exception * @throws Exception
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@AuthorizationWrapper(value = @RequiresPermissions("delete"), name = "删除")
public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{ public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{
boolean success = roleService.deleteRole(id); boolean success = roleService.deleteRole(id);
if(success){ if(success){
@ -147,6 +134,34 @@ public class RoleController extends BaseCrudRestController {
} }
} }
/***
* 显示创建页面
* @return
* @throws Exception
*/
@GetMapping("/toCreatePage")
public JsonResult toCreatePage(HttpServletRequest request, ModelMap modelMap)
throws Exception{
List<Permission> menuList = roleService.getAllMenu();
modelMap.put("menuList", menuList);
return new JsonResult(modelMap);
}
/***
* 显示更新页面
* @return
* @throws Exception
*/
@GetMapping("/toUpdatePage/{id}")
public JsonResult toUpdatePage(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{
RoleVO roleVO = roleService.toUpdatePage(id);
return new JsonResult(roleVO);
}
/*** /***
* 获取所有菜单,以及每个菜单下的所有权限 * 获取所有菜单,以及每个菜单下的所有权限
* @return * @return

View File

@ -2,6 +2,7 @@ package com.diboot.shiro.entity;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.diboot.core.entity.BaseEntity; import com.diboot.core.entity.BaseEntity;
import com.diboot.core.util.V;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
@ -34,4 +35,11 @@ public class Role extends BaseEntity {
@TableField(exist = false) @TableField(exist = false)
private List<Permission> permissionList; private List<Permission> permissionList;
/***
* 是否是管理员权限
* @return
*/
public boolean isAdmin(){
return V.equals(code, "ADMIN");
}
} }

View File

@ -13,12 +13,14 @@ import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View File

@ -16,6 +16,7 @@ public class ProxyToTargetObjectHelper {
/** /**
* 获取代理对象的目标对象 * 获取代理对象的目标对象
* 对象可能被多次代理所以需要递归获取原始对象
* @param proxy * @param proxy
* @return * @return
* @throws Exception * @throws Exception
@ -27,12 +28,13 @@ public class ProxyToTargetObjectHelper {
} }
//判断是jdk动态代理还是cglib代理 //判断是jdk动态代理还是cglib代理
if(AopUtils.isJdkDynamicProxy(proxy)) { if(AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy); proxy = getJdkDynamicProxyTargetObject(proxy);
} }
//cglib //cglib
else { else {
return getCglibProxyTargetObject(proxy); proxy = getCglibProxyTargetObject(proxy);
} }
return getTarget(proxy);
} }