commit
db3e1cc37a
59
README.md
59
README.md
|
@ -7,57 +7,30 @@
|
|||
# diboot-v2
|
||||
diboot 2.0版本项目,实现: diboot-core全新内核 + diboot-devtools代码生成。
|
||||
|
||||
### diboot-core: 优化内核
|
||||
### diboot-core: 精简优化内核
|
||||
全新精简内核,主要实现单表CRUD和多表关联绑定的无SQL实现方案,并提供其他常用开发场景的简单封装。
|
||||
|
||||
#### 单表CRUD无SQL
|
||||
> 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器)
|
||||
#### 多表关联查询无SQL(适用于大多数场景,拆分成单表查询自动实现结果绑定)
|
||||
> 通过注解实现多数场景下的关联查询无SQL
|
||||
##### 1. 注解自动绑定元数据(枚举值)的显示值Label
|
||||
~~~java
|
||||
@BindMetadata(type="GENDER", field = "gender")
|
||||
private String genderLabel;
|
||||
~~~
|
||||
##### 2. 注解自动绑定其他表的字段
|
||||
~~~java
|
||||
// 支持关联条件+附加条件绑定字段
|
||||
@BindField(entity=Department.class, field="name", condition="department_id=id AND code IS NOT NULL")
|
||||
private String deptName;
|
||||
> 通过注解实现多数场景下的关联查询无SQL化自动绑定
|
||||
|
||||
##### 1. @BindMetadata 注解自动绑定元数据(枚举值)的显示值Label
|
||||
##### 2. @BindField 注解自动绑定其他表的字段
|
||||
##### 3. @BindEntity 注解自动绑定单个其他表实体Entity
|
||||
##### 4. @BindEntityList 注解自动绑定其他表实体集合List<Entity>
|
||||
|
||||
// 支持通过中间表的级联关联绑定字段
|
||||
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
||||
private String orgName;
|
||||
~~~
|
||||
##### 3. 注解自动绑定其他表实体Entity
|
||||
~~~java
|
||||
// 支持关联条件+附加条件绑定Entity
|
||||
@BindEntity(entity = Department.class, condition="department_id=id")
|
||||
private Department department;
|
||||
具体请查看: [diboot-core 注解自动绑定多表关联](https://github.com/dibo-software/diboot-v2/tree/master/diboot-core "注解自动绑定多表关联").
|
||||
|
||||
// 通过中间表的级联关联绑定Entity(支持附加条件)
|
||||
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.deleted=0")
|
||||
private Organization organization;
|
||||
~~~
|
||||
##### 4. 注解自动绑定其他表实体集合List<Entity>
|
||||
~~~java
|
||||
// 支持关联条件+附加条件绑定多个Entity
|
||||
@BindEntityList(entity = Department.class, condition = "id=parent_id")
|
||||
private List<Department> children;
|
||||
|
||||
// 通过中间表的 多对多关联 绑定Entity(支持附加条件)
|
||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||
private List<Role> roleList;
|
||||
~~~
|
||||
|
||||
> 本地运行example需先执行/resources/init-mysql.sql到数据库。
|
||||
|
||||
### diboot-devtools 代码生成工具
|
||||
提供数据库表管理功能
|
||||
|
||||
提供CRUD及关联绑定的相关代码生成(Entity,VO,Service,Mapper,Controller...)
|
||||
### diboot-shiro 基于RBAC+Shiro的权限认证模块
|
||||
RBAC的角色权限+基于Shiro的细粒度权限控制
|
||||
|
||||
### diboot-example: 示例
|
||||
各组件使用示例项目
|
||||
|
||||
...
|
||||
> 运行example需先执行/resources/init-mysql.sql到数据库。
|
||||
|
||||
### diboot-devtools 代码生成工具
|
||||
> 比 1.x 版本更强大的代码生成工具 ...
|
||||
|
||||
...
|
|
@ -0,0 +1,46 @@
|
|||
## diboot-core: 全新优化内核
|
||||
全新精简内核,主要实现:
|
||||
1. 多表关联的自动绑定, 实现单表CRUD和多表关联的无SQL化
|
||||
2. 提供其他常用开发场景的简单封装。
|
||||
|
||||
#### 单表CRUD无SQL
|
||||
> 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器)
|
||||
#### 多表关联查询无SQL(适用于大多数场景,拆分成单表查询自动实现结果绑定)
|
||||
> 通过注解实现多数场景下的关联查询无SQL
|
||||
##### 1. 注解自动绑定元数据(枚举值)的显示值Label
|
||||
~~~java
|
||||
@BindMetadata(type="GENDER", field = "gender")
|
||||
private String genderLabel;
|
||||
~~~
|
||||
##### 2. 注解自动绑定其他表的字段
|
||||
~~~java
|
||||
// 支持关联条件+附加条件绑定字段
|
||||
@BindField(entity=Department.class, field="name", condition="department_id=id AND parent_id>=0")
|
||||
private String deptName;
|
||||
|
||||
// 支持通过中间表的级联关联绑定字段
|
||||
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
||||
private String orgName;
|
||||
~~~
|
||||
##### 3. 注解自动绑定其他表实体Entity
|
||||
~~~java
|
||||
// 支持关联条件+附加条件绑定Entity
|
||||
@BindEntity(entity = Department.class, condition="department_id=id")
|
||||
private Department department;
|
||||
|
||||
// 通过中间表的级联关联绑定Entity(支持附加条件)
|
||||
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.deleted=0")
|
||||
private Organization organization;
|
||||
~~~
|
||||
##### 4. 注解自动绑定其他表实体集合List<Entity>
|
||||
~~~java
|
||||
// 支持关联条件+附加条件绑定多个Entity
|
||||
@BindEntityList(entity = Department.class, condition = "id=parent_id")
|
||||
private List<Department> children;
|
||||
|
||||
// 通过中间表的 多对多关联 绑定Entity(支持附加条件)
|
||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||
private List<Role> roleList;
|
||||
~~~
|
||||
|
||||
> 本地运行test单元测试需先执行/test/resources/init-{db}.sql到你的数据库(暂提供Mysql脚本)。
|
|
@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||
/**
|
||||
* 基础CRUD的父类Mapper
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/22
|
||||
* Copyright © www.dibo.ltd
|
||||
* @version v2.0
|
||||
* @date 2018/12/22
|
||||
*/
|
||||
public interface BaseCrudMapper<T> extends BaseMapper<T> {
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ import com.diboot.core.entity.Metadata;
|
|||
/**
|
||||
* 元数据Mapper
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/22
|
||||
* Copyright © www.dibo.ltd
|
||||
* @version v2.0
|
||||
* @date 2018/12/22
|
||||
*/
|
||||
public interface MetadataMapper extends BaseCrudMapper<Metadata> {
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.diboot.core.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.diboot.core.binding.EntityBinder;
|
||||
import com.diboot.core.binding.EntityListBinder;
|
||||
import com.diboot.core.binding.FieldBinder;
|
||||
|
@ -18,7 +19,7 @@ import java.util.Map;
|
|||
* @version 2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public interface BaseService<T>{
|
||||
public interface BaseService<T> {
|
||||
|
||||
/**
|
||||
* 获取Entity实体
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.diboot.core.config.BaseConfig;
|
|||
import com.diboot.core.config.Cons;
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.core.util.BeanUtils;
|
||||
import com.diboot.core.util.S;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.KeyValue;
|
||||
|
|
|
@ -22,7 +22,21 @@ public class V {
|
|||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(Object obj){
|
||||
return obj == null;
|
||||
if(obj instanceof String){
|
||||
return isEmpty((String)obj);
|
||||
}
|
||||
else if(obj instanceof Collection){
|
||||
return isEmpty((Collection)obj);
|
||||
}
|
||||
else if(obj instanceof Map){
|
||||
return isEmpty((Map)obj);
|
||||
}
|
||||
else if(obj instanceof String[]){
|
||||
return isEmpty((String[])obj);
|
||||
}
|
||||
else{
|
||||
return obj == null;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -67,7 +81,21 @@ public class V {
|
|||
* @return
|
||||
*/
|
||||
public static boolean notEmpty(Object obj){
|
||||
return obj != null;
|
||||
if(obj instanceof String){
|
||||
return notEmpty((String)obj);
|
||||
}
|
||||
else if(obj instanceof Collection){
|
||||
return notEmpty((Collection)obj);
|
||||
}
|
||||
else if(obj instanceof Map){
|
||||
return notEmpty((Map)obj);
|
||||
}
|
||||
else if(obj instanceof String[]){
|
||||
return notEmpty((String[])obj);
|
||||
}
|
||||
else{
|
||||
return obj != null;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package diboot.core.test.binder;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.diboot.core.binding.manager.AnnotationBindingManager;
|
||||
import com.diboot.core.util.JSON;
|
||||
import com.diboot.core.util.V;
|
||||
import diboot.core.test.StartupApplication;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
import diboot.core.test.binder.service.UserService;
|
||||
import diboot.core.test.binder.vo.EntityBinderVO;
|
||||
import diboot.core.test.binder.vo.FieldBinderVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 测试字段绑定
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/06/22
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = {StartupApplication.class})
|
||||
public class TestEntityBinder {
|
||||
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
@Test
|
||||
public void testBinder(){
|
||||
// 加载测试数据
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||
List<User> userList = userService.getEntityList(queryWrapper);
|
||||
// 自动绑定
|
||||
List<EntityBinderVO> voList = AnnotationBindingManager.autoConvertAndBind(userList, EntityBinderVO.class);
|
||||
// 验证绑定结果
|
||||
if(V.notEmpty(voList)){
|
||||
for(EntityBinderVO vo : voList){
|
||||
// 验证直接关联和通过中间表间接关联的绑定
|
||||
Assert.assertEquals(vo.getDepartmentId(), vo.getDepartment().getId());
|
||||
Assert.assertNotNull(vo.getDepartment().getOrgId());
|
||||
Assert.assertNotNull(vo.getOrganization());
|
||||
System.out.println(JSON.stringify(vo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package diboot.core.test.binder;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.diboot.core.binding.manager.AnnotationBindingManager;
|
||||
import com.diboot.core.util.JSON;
|
||||
import com.diboot.core.util.V;
|
||||
import diboot.core.test.StartupApplication;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
import diboot.core.test.binder.service.DepartmentService;
|
||||
import diboot.core.test.binder.service.UserService;
|
||||
import diboot.core.test.binder.vo.EntityBinderVO;
|
||||
import diboot.core.test.binder.vo.EntityListComplexBinderVO;
|
||||
import diboot.core.test.binder.vo.EntityListSimpleBinderVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 测试字段绑定
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/06/22
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = {StartupApplication.class})
|
||||
public class TestEntityListBinder {
|
||||
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
@Autowired
|
||||
DepartmentService departmentService;
|
||||
|
||||
/**
|
||||
* 验证直接关联的绑定
|
||||
*/
|
||||
@Test
|
||||
public void testSimpleBinder(){
|
||||
// 加载测试数据
|
||||
LambdaQueryWrapper<Department> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Department::getId, 10001L);
|
||||
List<Department> entityList = departmentService.getEntityList(queryWrapper);
|
||||
// 自动绑定
|
||||
List<EntityListSimpleBinderVO> voList = AnnotationBindingManager.autoConvertAndBind(entityList, EntityListSimpleBinderVO.class);
|
||||
// 验证绑定结果
|
||||
if(V.notEmpty(voList)){
|
||||
for(EntityListSimpleBinderVO vo : voList){
|
||||
// 验证直接关联的绑定
|
||||
Assert.assertTrue(V.notEmpty(vo.getChildren()));
|
||||
System.out.println(JSON.stringify(vo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证通过中间表间接关联的绑定
|
||||
*/
|
||||
@Test
|
||||
public void testComplexBinder(){
|
||||
// 加载测试数据
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||
List<User> userList = userService.getEntityList(queryWrapper);
|
||||
// 自动绑定
|
||||
List<EntityListComplexBinderVO> voList = AnnotationBindingManager.autoConvertAndBind(userList, EntityListComplexBinderVO.class);
|
||||
// 验证绑定结果
|
||||
if(V.notEmpty(voList)){
|
||||
for(EntityListComplexBinderVO vo : voList){
|
||||
// 验证通过中间表间接关联的绑定
|
||||
Assert.assertTrue(V.notEmpty(vo.getRoleList()));
|
||||
System.out.println(JSON.stringify(vo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package diboot.core.test.binder;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.diboot.core.binding.manager.AnnotationBindingManager;
|
||||
import com.diboot.core.util.JSON;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.Pagination;
|
||||
import diboot.core.test.StartupApplication;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
import diboot.core.test.binder.service.UserService;
|
||||
import diboot.core.test.binder.vo.FieldBinderVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 测试字段绑定
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/06/22
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = {StartupApplication.class})
|
||||
public class TestFieldBinder {
|
||||
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
@Test
|
||||
public void testBinder(){
|
||||
// 加载测试数据
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||
List<User> userList = userService.getEntityList(queryWrapper);
|
||||
// 自动绑定
|
||||
List<FieldBinderVO> voList = AnnotationBindingManager.autoConvertAndBind(userList, FieldBinderVO.class);
|
||||
// 验证绑定结果
|
||||
if(V.notEmpty(voList)){
|
||||
for(FieldBinderVO vo : voList){
|
||||
// 验证直接关联和通过中间表间接关联的绑定
|
||||
Assert.assertNotNull(vo.getDeptName());
|
||||
Assert.assertNotNull(vo.getOrgName());
|
||||
Assert.assertNotNull(vo.getOrgTelphone());
|
||||
// 验证枚举值已绑定
|
||||
Assert.assertNotNull(vo.getGenderLabel());
|
||||
|
||||
System.out.println(JSON.stringify(vo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package diboot.core.test.binder.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
|
||||
/**
|
||||
* 定时任务
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2018/12/27
|
||||
*/
|
||||
public class Department extends BaseEntity {
|
||||
private static final long serialVersionUID = -4849732665419794547L;
|
||||
|
||||
@TableField
|
||||
private Long parentId;
|
||||
|
||||
@TableField
|
||||
private Long orgId;
|
||||
|
||||
@TableField
|
||||
private String name;
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Long getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(Long orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package diboot.core.test.binder.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
|
||||
/**
|
||||
* 单位Entity
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/5
|
||||
*/
|
||||
public class Organization extends BaseEntity {
|
||||
private static final long serialVersionUID = -5889309041570465909L;
|
||||
|
||||
@TableField
|
||||
private Long parentId;
|
||||
|
||||
@TableField
|
||||
private String name;
|
||||
|
||||
@TableField
|
||||
private String telphone;
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getTelphone() {
|
||||
return telphone;
|
||||
}
|
||||
|
||||
public void setTelphone(String telphone) {
|
||||
this.telphone = telphone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package diboot.core.test.binder.entity;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/30
|
||||
*/
|
||||
public class Role extends BaseEntity {
|
||||
private static final long serialVersionUID = 3701095453152116088L;
|
||||
|
||||
private String name;
|
||||
|
||||
private String code;
|
||||
|
||||
@JSONField(serialize = false)
|
||||
public Date createTime;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package diboot.core.test.binder.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
|
||||
/**
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/30
|
||||
*/
|
||||
public class User extends BaseEntity {
|
||||
private static final long serialVersionUID = 3050761344045195972L;
|
||||
|
||||
@TableField
|
||||
private Long departmentId;
|
||||
|
||||
@TableField
|
||||
private String username;
|
||||
|
||||
@TableField
|
||||
private String gender;
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package diboot.core.test.binder.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
|
||||
/**
|
||||
* 部门Mapper
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/22
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
public interface DepartmentMapper extends BaseCrudMapper<Department> {
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package diboot.core.test.binder.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import diboot.core.test.binder.entity.Organization;
|
||||
|
||||
/**
|
||||
* 单位Mapper
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/22
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
public interface OrganizationMapper extends BaseCrudMapper<Organization> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package diboot.core.test.binder.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import diboot.core.test.binder.entity.Role;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
/**
|
||||
* 员工Mapper
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/22
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
public interface RoleMapper extends BaseCrudMapper<Role> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package diboot.core.test.binder.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
/**
|
||||
* 员工Mapper
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/22
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
public interface UserMapper extends BaseCrudMapper<User> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package diboot.core.test.binder.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
|
||||
/**
|
||||
* 部门相关Service
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/30
|
||||
*/
|
||||
public interface DepartmentService extends BaseService<Department> {
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package diboot.core.test.binder.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import diboot.core.test.binder.entity.Organization;
|
||||
|
||||
/**
|
||||
* 单位相关Service
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/5
|
||||
*/
|
||||
public interface OrganizationService extends BaseService<Organization> {
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package diboot.core.test.binder.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import diboot.core.test.binder.entity.Role;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
/**
|
||||
* 角色相关Service
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/5
|
||||
*/
|
||||
public interface RoleService extends BaseService<Role> {
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package diboot.core.test.binder.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
/**
|
||||
* 员工相关Service
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/5
|
||||
*/
|
||||
public interface UserService extends BaseService<User> {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package diboot.core.test.binder.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
import diboot.core.test.binder.mapper.DepartmentMapper;
|
||||
import diboot.core.test.binder.service.DepartmentService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 部门相关Service实现
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/30
|
||||
*/
|
||||
@Service
|
||||
public class DepartmentServiceImpl extends BaseServiceImpl<DepartmentMapper, Department> implements DepartmentService {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package diboot.core.test.binder.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import diboot.core.test.binder.entity.Organization;
|
||||
import diboot.core.test.binder.mapper.OrganizationMapper;
|
||||
import diboot.core.test.binder.service.OrganizationService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 单位相关Service实现
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/23
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
@Service
|
||||
public class OrganizationServiceImpl extends BaseServiceImpl<OrganizationMapper, Organization> implements OrganizationService {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package diboot.core.test.binder.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import diboot.core.test.binder.entity.Role;
|
||||
import diboot.core.test.binder.mapper.RoleMapper;
|
||||
import diboot.core.test.binder.service.RoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 员工相关Service
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/23
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
@Service
|
||||
public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, Role> implements RoleService {
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package diboot.core.test.binder.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
import diboot.core.test.binder.mapper.UserMapper;
|
||||
import diboot.core.test.binder.service.UserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 员工相关Service
|
||||
* @author Mazhicheng
|
||||
* @version 2018/12/23
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package diboot.core.test.binder.vo;
|
||||
|
||||
import com.diboot.core.binding.annotation.BindEntity;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
import diboot.core.test.binder.entity.Organization;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
/**
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/30
|
||||
*/
|
||||
public class EntityBinderVO extends User {
|
||||
private static final long serialVersionUID = 3526115343377985725L;
|
||||
|
||||
// 字段关联,相同条件的entity+condition将合并为一条SQL查询
|
||||
@BindEntity(entity= Department.class, condition="this.department_id=id AND parent_id >= 0")
|
||||
private Department department;
|
||||
|
||||
// 通过中间表关联Entity
|
||||
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id") // AND ...
|
||||
private Organization organization;
|
||||
|
||||
|
||||
public Department getDepartment() {
|
||||
return department;
|
||||
}
|
||||
public void setDepartment(Department department) {
|
||||
this.department = department;
|
||||
}
|
||||
public Organization getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
public void setOrganization(Organization organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package diboot.core.test.binder.vo;
|
||||
|
||||
import com.diboot.core.binding.annotation.BindEntityList;
|
||||
import diboot.core.test.binder.entity.Role;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <Description>
|
||||
*
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/06/22
|
||||
*/
|
||||
public class EntityListComplexBinderVO extends User {
|
||||
|
||||
// 支持通过中间表的多-多Entity实体关联
|
||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||
private List<Role> roleList;
|
||||
|
||||
public List<Role> getRoleList() {
|
||||
return roleList;
|
||||
}
|
||||
|
||||
public void setRoleList(List<Role> roleList) {
|
||||
this.roleList = roleList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package diboot.core.test.binder.vo;
|
||||
|
||||
import com.diboot.core.binding.annotation.BindEntityList;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/5
|
||||
*/
|
||||
public class EntityListSimpleBinderVO extends Department {
|
||||
private static final long serialVersionUID = -362116388664907913L;
|
||||
|
||||
// 直接关联多个Entity
|
||||
@BindEntityList(entity = Department.class, condition = "this.id=parent_id")
|
||||
private List<Department> children;
|
||||
|
||||
public List<Department> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<Department> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package diboot.core.test.binder.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.binding.annotation.BindField;
|
||||
import com.diboot.core.binding.annotation.BindMetadata;
|
||||
import diboot.core.test.binder.entity.Department;
|
||||
import diboot.core.test.binder.entity.Organization;
|
||||
import diboot.core.test.binder.entity.User;
|
||||
|
||||
/**
|
||||
* <Description>
|
||||
*
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/06/22
|
||||
*/
|
||||
public class FieldBinderVO extends User{
|
||||
private static final long serialVersionUID = 3526115343377985725L;
|
||||
|
||||
// 字段关联,相同条件的entity+condition将合并为一条SQL查询
|
||||
@BindField(entity= Department.class, field="name", condition="this.department_id=id AND parent_id IS NOT NULL")
|
||||
private String deptName;
|
||||
|
||||
// 支持级联字段关联,相同条件的entity+condition将合并为一条SQL查询
|
||||
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
||||
private String orgName;
|
||||
@BindField(entity = Organization.class, field="telphone", condition="this.department_id=department.id AND department.org_id=id")
|
||||
private String orgTelphone;
|
||||
|
||||
// 绑定元数据枚举
|
||||
@BindMetadata(type="GENDER", field = "gender")
|
||||
private String genderLabel;
|
||||
|
||||
public String getDeptName() {
|
||||
return deptName;
|
||||
}
|
||||
public void setDeptName(String deptName) {
|
||||
this.deptName = deptName;
|
||||
}
|
||||
public String getOrgName() {
|
||||
return orgName;
|
||||
}
|
||||
public void setOrgName(String orgName) {
|
||||
this.orgName = orgName;
|
||||
}
|
||||
public String getOrgTelphone() {
|
||||
return orgTelphone;
|
||||
}
|
||||
public void setOrgTelphone(String orgTelphone) {
|
||||
this.orgTelphone = orgTelphone;
|
||||
}
|
||||
|
||||
public String getGenderLabel() {
|
||||
return genderLabel;
|
||||
}
|
||||
|
||||
public void setGenderLabel(String genderLabel) {
|
||||
this.genderLabel = genderLabel;
|
||||
}
|
||||
}
|
|
@ -32,8 +32,8 @@ import java.util.List;
|
|||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableTransactionManagement(proxyTargetClass=true)
|
||||
@ComponentScan(basePackages={"com.diboot"})
|
||||
@MapperScan({"com.diboot.**.mapper"})
|
||||
@ComponentScan(basePackages={"com.diboot", "diboot.core"})
|
||||
@MapperScan({"com.diboot.**.mapper", "diboot.**.mapper"})
|
||||
public class SpringMvcConfig implements WebMvcConfigurer{
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class);
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
-- create schema diboot_example collate utf8_general_ci;
|
||||
-- 初始化表
|
||||
create table department
|
||||
(
|
||||
id bigint unsigned not null comment 'ID'
|
||||
primary key,
|
||||
parent_id bigint default 0 not null comment '上级部门ID',
|
||||
org_id bigint not null comment '单位ID',
|
||||
name varchar(50) not null comment '名称',
|
||||
extdata varchar(100) null comment '扩展字段',
|
||||
deleted tinyint(1) default 0 not null comment '已删除',
|
||||
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
|
||||
)
|
||||
comment '组织单位' charset=utf8mb4;
|
||||
|
||||
create table metadata
|
||||
(
|
||||
id int unsigned auto_increment comment 'ID'
|
||||
primary key,
|
||||
parent_id int unsigned not null comment '父ID',
|
||||
type varchar(50) not null comment '元数据类型',
|
||||
item_name varchar(100) not null comment '元数据项显示名',
|
||||
item_value varchar(100) null comment '元数据项存储值',
|
||||
comment varchar(200) null comment '备注',
|
||||
extdata varchar(200) null comment '扩展属性',
|
||||
sort_id smallint(6) default 99 not null comment '排序号',
|
||||
`system` tinyint(1) default 0 not null comment '是否是系统预置',
|
||||
editable tinyint(1) default 1 not null comment '是否可编辑',
|
||||
deleted tinyint(1) default 0 not null comment '已删除',
|
||||
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
|
||||
);
|
||||
|
||||
create table organization
|
||||
(
|
||||
id int auto_increment comment 'ID'
|
||||
primary key,
|
||||
parent_id int default 0 not null comment '上级单位ID',
|
||||
name varchar(100) not null comment '名称',
|
||||
telphone varchar(20) null comment '电话',
|
||||
deleted tinyint(1) default 0 not null comment '是否有效',
|
||||
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
|
||||
)
|
||||
comment '组织单位' charset=utf8mb4;
|
||||
|
||||
create table role
|
||||
(
|
||||
id int auto_increment comment 'ID'
|
||||
primary key,
|
||||
name varchar(20) null,
|
||||
code varchar(20) null,
|
||||
deleted tinyint(1) default 0 null,
|
||||
create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间'
|
||||
);
|
||||
|
||||
create table user
|
||||
(
|
||||
id int auto_increment comment 'ID'
|
||||
primary key,
|
||||
department_id int default 0 not null,
|
||||
username varchar(20) null,
|
||||
gender varchar(20) null,
|
||||
deleted tinyint(1) default 0 null,
|
||||
create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间'
|
||||
);
|
||||
|
||||
create table user_role
|
||||
(
|
||||
user_id int not null comment '用户ID',
|
||||
role_id int not null comment '角色ID',
|
||||
primary key (user_id, role_id)
|
||||
);
|
||||
|
||||
-- 初始化样例数据
|
||||
INSERT INTO department (id, parent_id, org_id, name)
|
||||
VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组');
|
||||
|
||||
INSERT INTO metadata (id, parent_id, type, item_name, item_value, comment, extdata, sort_id, `system`, editable)
|
||||
VALUES (1, 0, 'GENDER', '性别', null, '', null, 99, 1, 1), (2, 1, 'GENDER', '男', 'M', null, null, 99, 1, 0), (3, 1, 'GENDER', '女', 'F', null, null, 99, 1, 0);
|
||||
|
||||
INSERT INTO organization (id, parent_id, name, telphone) VALUES (100001, 0, '苏州帝博', '0512-62988949');
|
||||
|
||||
INSERT INTO role (id, name, code) VALUES (101, '管理员', 'ADMIN'), (102, '操作员', 'OPERATOR');
|
||||
|
||||
INSERT INTO user (id, department_id, username, gender)
|
||||
VALUES (1001, 10002, '张三', 'M'), (1002, 10002, '李四', 'F');
|
||||
|
||||
INSERT INTO user_role (user_id, role_id) VALUES (1001, 101),(1001, 102),(1002, 102);
|
|
@ -0,0 +1,2 @@
|
|||
## diboot-example: 各组件/模块的使用样例
|
||||
本地运行test单元测试需先执行/test/resources/init-{db}.sql到你的数据库(暂提供Mysql脚本)。
|
|
@ -2,6 +2,7 @@ dependencies {
|
|||
|
||||
compile project(":diboot-core")
|
||||
compile project(":diboot-shiro")
|
||||
compile project(":diboot-shiro-wx-mp")
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
|
@ -1,11 +1,18 @@
|
|||
package com.diboot.example.controller;
|
||||
|
||||
import com.diboot.core.config.BaseConfig;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.JsonResult;
|
||||
import com.diboot.core.vo.Status;
|
||||
import com.diboot.shiro.jwt.BaseJwtAuthenticationToken;
|
||||
import com.diboot.shiro.config.AuthType;
|
||||
import com.diboot.shiro.entity.SysUser;
|
||||
import com.diboot.shiro.service.AuthWayService;
|
||||
import com.diboot.shiro.util.JwtHelper;
|
||||
import com.diboot.shiro.wx.mp.service.WxMpServiceExt;
|
||||
import me.chanjar.weixin.common.api.WxConsts;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpUser;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -23,9 +30,14 @@ public class AuthTokenController {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthTokenController.class);
|
||||
|
||||
private static final String STATE = BaseConfig.getProperty("wechat.state");
|
||||
|
||||
@Autowired
|
||||
private Map<String, AuthWayService> authWayServiceMap;
|
||||
|
||||
@Autowired
|
||||
private WxMpServiceExt wxMpService;
|
||||
|
||||
/***
|
||||
* 用户名密码登录接口
|
||||
* @param sysUser
|
||||
|
@ -57,6 +69,95 @@ public class AuthTokenController {
|
|||
return new JsonResult(Status.FAIL_INVALID_TOKEN, errorMsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建OAuth认证链接(为了获取授权所需code)
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@GetMapping("/buildOAuthUrl")
|
||||
public JsonResult buildOAuthUrl4mp(HttpServletRequest request) throws Exception{
|
||||
String url = request.getParameter("url");
|
||||
if (V.isEmpty(url)){
|
||||
return new JsonResult(Status.FAIL_OPERATION, new String[]{"url为空,获取OAuth链接失败"});
|
||||
}
|
||||
|
||||
String oauthUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_USERINFO, STATE);
|
||||
return new JsonResult(Status.OK, oauthUrl, new String[]{"获取OAuth链接成功"});
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号的回调授权登录认证
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/apply")
|
||||
public JsonResult applyTokenByOAuth2mp(HttpServletRequest request) throws Exception{
|
||||
String code = request.getParameter("code");
|
||||
String state = request.getParameter("state");
|
||||
String openid = "";
|
||||
if (JwtHelper.isRequestTokenEffective(request)){
|
||||
String account = JwtHelper.getAccountFromToken(JwtHelper.getRequestToken(request));
|
||||
if (account == null){
|
||||
// 如果有code并且token已过期,则使用code获取openid
|
||||
if (V.isEmpty(code)){
|
||||
return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{"token已过期"});
|
||||
}
|
||||
} else {
|
||||
openid = account;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果openid没有通过token获取到,则通过code获取
|
||||
if (V.isEmpty(openid)){
|
||||
// 校验STATE
|
||||
if (V.notEmpty(STATE) && !STATE.equals(state)){
|
||||
return new JsonResult(Status.FAIL_OPERATION, new String[]{"非法来源"});
|
||||
}
|
||||
// 获取wxMpService
|
||||
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);
|
||||
if (!wxMpService.oauth2validateAccessToken(wxMpOAuth2AccessToken)){
|
||||
wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken());
|
||||
}
|
||||
WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);
|
||||
|
||||
openid = wxMpUser.getOpenId();
|
||||
}
|
||||
|
||||
// 如果没有获取到wechat,则返回提示信息
|
||||
if (V.isEmpty(openid)){
|
||||
return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{"获取信息失败"});
|
||||
}
|
||||
|
||||
// 设置token
|
||||
BaseJwtAuthenticationToken authToken = new BaseJwtAuthenticationToken(authWayServiceMap, openid, AuthType.WX_MP);
|
||||
// 获取当前的Subject
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
String token = null;
|
||||
String errorMsg = null;
|
||||
|
||||
try {
|
||||
subject.login(authToken);
|
||||
//验证是否登录成功
|
||||
if(subject.isAuthenticated()){
|
||||
token = (String)authToken.getCredentials();
|
||||
logger.debug("openid[" + openid + "]申请token成功!authtoken="+token);
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
logger.error("登录失败", e);
|
||||
}
|
||||
|
||||
if (V.isEmpty(token)){
|
||||
String msg = V.notEmpty(errorMsg) ? errorMsg : "申请token失败";
|
||||
return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{msg});
|
||||
}
|
||||
|
||||
return new JsonResult(Status.OK, token, new String[]{"申请token成功"});
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public JsonResult logout(HttpServletRequest request, HttpServletResponse response) throws Exception{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
@ -66,4 +167,5 @@ public class AuthTokenController {
|
|||
|
||||
return new JsonResult(Status.OK, new String[]{"退出登录成功"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.diboot.example.mapper.SysUserMapper">
|
||||
|
||||
</mapper>
|
|
@ -26,7 +26,7 @@ public class UserVO extends User {
|
|||
private String genderLabel;
|
||||
|
||||
// 字段关联,相同条件的entity+condition将合并为一条SQL查询
|
||||
@BindField(entity=Department.class, field="name", condition="this.department_id=id AND code IS NOT NULL")
|
||||
@BindField(entity=Department.class, field="name", condition="this.department_id=id AND parent_id IS NOT NULL")
|
||||
private String deptName;
|
||||
@BindField(entity=Department.class, field="code", condition="this.department_id=id")
|
||||
private String deptCode;
|
||||
|
@ -42,7 +42,7 @@ public class UserVO extends User {
|
|||
private Organization organization;
|
||||
|
||||
// 支持通过中间表的多-多Entity实体关联
|
||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||
private List<Role> roleList;
|
||||
//@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||
//private List<Role> roleList;
|
||||
|
||||
}
|
|
@ -7,7 +7,6 @@ create table department
|
|||
parent_id bigint default 0 not null comment '上级部门ID',
|
||||
org_id bigint not null comment '单位ID',
|
||||
name varchar(50) not null comment '名称',
|
||||
code varchar(20) null comment '编码',
|
||||
extdata varchar(100) null comment '扩展字段',
|
||||
deleted tinyint(1) default 0 not null comment '已删除',
|
||||
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
|
||||
|
@ -38,7 +37,6 @@ create table organization
|
|||
parent_id int default 0 not null comment '上级单位ID',
|
||||
name varchar(100) not null comment '名称',
|
||||
telphone varchar(20) null comment '电话',
|
||||
address varchar(255) null comment '地址',
|
||||
deleted tinyint(1) default 0 not null comment '是否有效',
|
||||
create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间'
|
||||
)
|
||||
|
@ -73,13 +71,13 @@ create table user_role
|
|||
);
|
||||
|
||||
-- 初始化样例数据
|
||||
INSERT INTO department (id, parent_id, org_id, name, code)
|
||||
VALUES (10001, 0, 100001, '产品部', 'PROD'), (10002, 10001, 100001, '研发组', 'DEV'), (10003, 10001, 100001, '测试组', 'TST');
|
||||
INSERT INTO department (id, parent_id, org_id, name)
|
||||
VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组');
|
||||
|
||||
INSERT INTO metadata (id, parent_id, type, item_name, item_value, comment, extdata, sort_id, `system`, editable)
|
||||
VALUES (1, 0, 'GENDER', '性别', null, '', null, 99, 1, 1), (2, 1, 'GENDER', '男', 'M', null, null, 99, 1, 0), (3, 1, 'GENDER', '女', 'F', null, null, 99, 1, 0);
|
||||
|
||||
INSERT INTO organization (id, parent_id, name, telphone, address) VALUES (100001, 0, '苏州帝博', '0512-62988949', '江苏苏州');
|
||||
INSERT INTO organization (id, parent_id, name, telphone) VALUES (100001, 0, '苏州帝博', '0512-62988949');
|
||||
|
||||
INSERT INTO role (id, name, code) VALUES (101, '管理员', 'ADMIN'), (102, '操作员', 'OPERATOR');
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
/**
|
||||
* @author : wee
|
||||
* @Description: todo
|
||||
* @Date 2019-05-17 19:15
|
||||
*/
|
||||
public class MainTest {
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.diboot.example;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class ApplicationTest {
|
||||
|
||||
@Test
|
||||
public void contextLoads(){
|
||||
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.diboot.example.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.diboot.core.util.BeanUtils;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.example.ApplicationTest;
|
||||
import com.diboot.example.entity.Department;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@Component
|
||||
public class DepartmentServiceTest extends ApplicationTest {
|
||||
|
||||
@Autowired
|
||||
private DepartmentService departmentService;
|
||||
|
||||
@Test
|
||||
public void getEntityListTest() throws Exception{
|
||||
QueryWrapper<Department> query = new QueryWrapper<>();
|
||||
query.eq(BeanUtils.convertToFieldName(Department::getName), "研发部");
|
||||
List<Department> departmentList = departmentService.getEntityList(query);
|
||||
Assert.assertTrue(V.notEmpty(departmentList));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'com.diboot'
|
||||
version '2.0-alpha'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
compile project(":diboot-core")
|
||||
compile project(":diboot-shiro")
|
||||
|
||||
// 微信开发组件
|
||||
compile("com.github.binarywang:weixin-java-mp:3.2.0")
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.diboot.shiro.wx.mp.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Data
|
||||
public class WxMpMember extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = -106928701430810778L;
|
||||
|
||||
@TableField
|
||||
private String openid;
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.diboot.shiro.wx.mp.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
public interface WxMpMemberMapper extends BaseCrudMapper<WxMpMember> {
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.diboot.example.mapper.DepartmentMapper">
|
||||
<mapper namespace="com.diboot.shiro.wx.mp.mapper.WxMpMemberMapper">
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,13 @@
|
|||
package com.diboot.shiro.wx.mp.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
public interface WxMpMemberService extends BaseService<WxMpMember> {
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.diboot.shiro.wx.mp.service;
|
||||
|
||||
import com.diboot.core.config.BaseConfig;
|
||||
import com.diboot.core.util.V;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
|
||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
|
||||
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class WxMpServiceExt extends WxMpServiceImpl {
|
||||
private static final Logger logger = LoggerFactory.getLogger(WxMpServiceExt.class);
|
||||
|
||||
private WxMpMessageRouter wxMpMessageRouter;
|
||||
|
||||
private static final String appId = BaseConfig.getProperty("wechat.appId");
|
||||
private static final String secret = BaseConfig.getProperty("wechat.secret");
|
||||
private static final String token = BaseConfig.getProperty("wechat.token");
|
||||
private static final String aesKey = BaseConfig.getProperty("wechat.aesKey");
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
WxMpInMemoryConfigStorage inMemoryConfigStorage = new WxMpInMemoryConfigStorage();
|
||||
|
||||
if (V.isEmpty(appId) || V.isEmpty(secret)){
|
||||
logger.warn("服务号相关的appid或secret为空,请检查application.properties配置文件");
|
||||
}
|
||||
|
||||
inMemoryConfigStorage.setAppId(appId);
|
||||
inMemoryConfigStorage.setSecret(secret);
|
||||
inMemoryConfigStorage.setToken(token);
|
||||
inMemoryConfigStorage.setAesKey(aesKey);
|
||||
|
||||
setWxMpConfigStorage(inMemoryConfigStorage);
|
||||
|
||||
wxMpMessageRouter = new WxMpMessageRouter(this);
|
||||
wxMpMessageRouter.rule().handler(new WxMpMessageHandler() {
|
||||
@Override
|
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
|
||||
logger.info("\n接收到 {} 公众号请求消息,内容:{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage);
|
||||
return null;
|
||||
}
|
||||
}).next();
|
||||
}
|
||||
|
||||
public WxMpMessageRouter getWxMpMessageRouter(){
|
||||
return wxMpMessageRouter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.diboot.shiro.wx.mp.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.shiro.config.AuthType;
|
||||
import com.diboot.shiro.jwt.BaseJwtAuthenticationToken;
|
||||
import com.diboot.shiro.service.AuthWayService;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
import com.diboot.shiro.wx.mp.service.WxMpMemberService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/***
|
||||
* 微信公众号认证实现
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Service
|
||||
public class WxMpAuthWayServiceImpl implements AuthWayService {
|
||||
|
||||
@Autowired
|
||||
private WxMpMemberService wxMpMemberService;
|
||||
|
||||
private AuthType authType = AuthType.WX_MP;
|
||||
|
||||
private BaseJwtAuthenticationToken token;
|
||||
|
||||
@Override
|
||||
public AuthType authType() {
|
||||
return authType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initByToken(BaseJwtAuthenticationToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getUser() {
|
||||
QueryWrapper<WxMpMember> query = new QueryWrapper();
|
||||
query.lambda()
|
||||
.eq(WxMpMember::getOpenid, token.getAccount());
|
||||
|
||||
List<WxMpMember> wxMpMemberList = wxMpMemberService.getEntityList(query);
|
||||
if (V.isEmpty(wxMpMemberList)){
|
||||
return null;
|
||||
}
|
||||
return wxMpMemberList.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requirePassword() {
|
||||
return authType.isRequirePassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPasswordMatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreliminaryVerified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpiresInMinutes() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.diboot.shiro.wx.mp.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import com.diboot.shiro.wx.mp.entity.WxMpMember;
|
||||
import com.diboot.shiro.wx.mp.mapper.WxMpMemberMapper;
|
||||
import com.diboot.shiro.wx.mp.service.WxMpMemberService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/***
|
||||
* @author Wangyl
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class WxMpMemberServiceImpl extends BaseServiceImpl<WxMpMemberMapper, WxMpMember> implements WxMpMemberService {
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package com.diboot.shiro.bind.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* controller上注解,用于标记权限的菜单分类
|
||||
* @author : wee
|
||||
* @version v2.0
|
||||
* @Date 2019-06-14 23:00
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface PermissionsMenu {
|
||||
/**
|
||||
* 菜单编码
|
||||
* @return
|
||||
*/
|
||||
String menuCode();
|
||||
/**
|
||||
* 菜单名称
|
||||
* @return
|
||||
*/
|
||||
String menuName();
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.diboot.shiro.bind.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 权限注解的前缀,用于controller注解
|
||||
*
|
||||
* @author : wee
|
||||
* @version v 2.0
|
||||
* @Date 2019-06-17 20:42
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface PermissionsPrefix {
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*
|
||||
* 设置当前权限前缀名称
|
||||
* @return
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* 设置当前权限前缀编码
|
||||
* @return
|
||||
*/
|
||||
String code();
|
||||
|
||||
/**
|
||||
* <h3>{@link RequiresPermissionsWrapper#value()}的前缀</h3>
|
||||
* <ul>
|
||||
* <li> value = permissions</li>
|
||||
* <li>{@link RequiresPermissionsWrapper#value()} = {"list", "get"}</li>
|
||||
* <li>实际权限为:{"permissions:list", "permissions:list"}</li>
|
||||
* </ul>
|
||||
* 注:当前注解优先级低于{@link RequiresPermissionsWrapper#prefix()},
|
||||
* 如果两者都配置优先使用{@link RequiresPermissionsWrapper#prefix()}
|
||||
* @return
|
||||
*/
|
||||
String prefix() default "";
|
||||
}
|
|
@ -6,7 +6,7 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
|
|||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 注解{@link RequiresPermissions}的增强注解,增加权限描述等字段
|
||||
* 注解{@link RequiresPermissions}的包装,增加权限描述等字段
|
||||
* @author : wee
|
||||
* @version v2.0
|
||||
* @Date 2019-06-14 17:50
|
||||
|
@ -14,35 +14,31 @@ import java.lang.annotation.*;
|
|||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@RequiresPermissions("default")
|
||||
public @interface RequiresPermissionsProxy {
|
||||
public @interface RequiresPermissionsWrapper {
|
||||
|
||||
/**
|
||||
* 代理 {@link RequiresPermissions#value()}
|
||||
* 包装 {@link RequiresPermissions#value()}
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* 代理 {@link RequiresPermissions#logical()}
|
||||
* 包装 {@link RequiresPermissions#logical()}
|
||||
*/
|
||||
Logical logical() default Logical.AND;
|
||||
|
||||
/**
|
||||
* 菜单编码
|
||||
* @return
|
||||
*/
|
||||
String menuCode();
|
||||
/**
|
||||
* 菜单名称
|
||||
* @return
|
||||
*/
|
||||
String menuName();
|
||||
|
||||
/**
|
||||
* 权限名称
|
||||
* @return
|
||||
*/
|
||||
String permissionName();
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 参照{@link PermissionsPrefix#prefix()}解释
|
||||
* @return
|
||||
*/
|
||||
String prefix() default "";
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.diboot.shiro.bind.aop;
|
||||
|
||||
import com.diboot.shiro.bind.aop.PermissionWrapperAnnotationMethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.shiro.aop.AnnotationResolver;
|
||||
import org.apache.shiro.authz.aop.*;
|
||||
import org.apache.shiro.spring.aop.SpringAnnotationResolver;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author : wee
|
||||
* @version : v2.0
|
||||
* @Date 2019-06-15 12:07
|
||||
*/
|
||||
public class CustomAopAllianceAnnotationsAuthorizingMethodInterceptor extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor {
|
||||
public CustomAopAllianceAnnotationsAuthorizingMethodInterceptor() {
|
||||
List<AuthorizingAnnotationMethodInterceptor> interceptors =
|
||||
new ArrayList<AuthorizingAnnotationMethodInterceptor>(6);
|
||||
AnnotationResolver resolver = new SpringAnnotationResolver();
|
||||
interceptors.add(new PermissionWrapperAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));
|
||||
|
||||
setMethodInterceptors(interceptors);
|
||||
}
|
||||
/**
|
||||
* Creates a {@link MethodInvocation MethodInvocation} that wraps an
|
||||
* {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} instance,
|
||||
* enabling Shiro Annotations in <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a> environments
|
||||
* (Spring, etc).
|
||||
*
|
||||
* @param implSpecificMethodInvocation AOP Alliance {@link org.aopalliance.intercept.MethodInvocation MethodInvocation}
|
||||
* @return a Shiro {@link MethodInvocation MethodInvocation} instance that wraps the AOP Alliance instance.
|
||||
*/
|
||||
protected org.apache.shiro.aop.MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) {
|
||||
final MethodInvocation mi = (MethodInvocation) implSpecificMethodInvocation;
|
||||
|
||||
return new org.apache.shiro.aop.MethodInvocation() {
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return mi.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArguments() {
|
||||
return mi.getArguments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Method invocation [" + mi.getMethod() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object proceed() throws Throwable {
|
||||
return mi.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getThis() {
|
||||
return mi.getThis();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply casts the method argument to an
|
||||
* {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} and then
|
||||
* calls <code>methodInvocation.{@link org.aopalliance.intercept.MethodInvocation#proceed proceed}()</code>
|
||||
*
|
||||
* @param aopAllianceMethodInvocation the {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation}
|
||||
* @return the {@link org.aopalliance.intercept.MethodInvocation#proceed() org.aopalliance.intercept.MethodInvocation.proceed()} method call result.
|
||||
* @throws Throwable if the underlying AOP Alliance <code>proceed()</code> call throws a <code>Throwable</code>.
|
||||
*/
|
||||
protected Object continueInvocation(Object aopAllianceMethodInvocation) throws Throwable {
|
||||
MethodInvocation mi = (MethodInvocation) aopAllianceMethodInvocation;
|
||||
return mi.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Shiro {@link MethodInvocation MethodInvocation} instance and then immediately calls
|
||||
* {@link org.apache.shiro.authz.aop.AuthorizingMethodInterceptor#invoke super.invoke}.
|
||||
*
|
||||
* @param methodInvocation the AOP Alliance-specific <code>methodInvocation</code> instance.
|
||||
* @return the return value from invoking the method invocation.
|
||||
* @throws Throwable if the underlying AOP Alliance method invocation throws a <code>Throwable</code>.
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
|
||||
org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
|
||||
return super.invoke(mi);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package com.diboot.shiro.bind.aop;
|
||||
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
|
||||
import org.apache.shiro.authz.annotation.*;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author : wee
|
||||
* @version : v1.0
|
||||
* @Date 2019-06-15 12:27
|
||||
*/
|
||||
public class CustomAuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CustomAuthorizationAttributeSourceAdvisor.class);
|
||||
|
||||
private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
|
||||
new Class[] {
|
||||
RequiresPermissionsWrapper.class,
|
||||
RequiresPermissions.class, RequiresRoles.class,
|
||||
RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
|
||||
};
|
||||
|
||||
protected SecurityManager securityManager = null;
|
||||
|
||||
/**
|
||||
* Create a new AuthorizationAttributeSourceAdvisor.
|
||||
*/
|
||||
public CustomAuthorizationAttributeSourceAdvisor() {
|
||||
setAdvice(new CustomAopAllianceAnnotationsAuthorizingMethodInterceptor());
|
||||
}
|
||||
|
||||
public SecurityManager getSecurityManager() {
|
||||
return securityManager;
|
||||
}
|
||||
|
||||
public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
|
||||
this.securityManager = securityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if the method or the class has any Shiro annotations, false otherwise.
|
||||
* The annotations inspected are:
|
||||
* <ul>
|
||||
* <li>{@link RequiresPermissionsWrapper RequiresPermissionsWrapper}</li>
|
||||
* <li>{@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}</li>
|
||||
* <li>{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}</li>
|
||||
* <li>{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}</li>
|
||||
* <li>{@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}</li>
|
||||
* <li>{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param method the method to check for a Shiro annotation
|
||||
* @param targetClass the class potentially declaring Shiro annotations
|
||||
* @return <tt>true</tt> if the method has a Shiro annotation, false otherwise.
|
||||
* @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, Class)
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
Method m = method;
|
||||
|
||||
if ( isAuthzAnnotationPresent(m) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//The 'method' parameter could be from an interface that doesn't have the annotation.
|
||||
//Check to see if the implementation has it.
|
||||
if ( targetClass != null) {
|
||||
try {
|
||||
m = targetClass.getMethod(m.getName(), m.getParameterTypes());
|
||||
return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
//default return value is false. If we can't find the method, then obviously
|
||||
//there is no annotation, so just use the default return value.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAuthzAnnotationPresent(Class<?> targetClazz) {
|
||||
for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
|
||||
Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass);
|
||||
if ( a != null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAuthzAnnotationPresent(Method method) {
|
||||
for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
|
||||
Annotation a = AnnotationUtils.findAnnotation(method, annClass);
|
||||
if ( a != null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package com.diboot.shiro.bind.aop;
|
||||
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy;
|
||||
import com.diboot.shiro.bind.handler.PermissionProxyAnnotationHandler;
|
||||
import org.apache.shiro.aop.AnnotationResolver;
|
||||
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
|
||||
|
||||
/**
|
||||
* {@link RequiresPermissionsProxy} 拦截器
|
||||
* @author : wee
|
||||
* @version : v2.0
|
||||
* @Date 2019-06-14 22:19
|
||||
*/
|
||||
public class PermissionProxyAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
|
||||
/**
|
||||
* Default no-argument constructor that ensures this interceptor looks for
|
||||
* {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations in a method declaration.
|
||||
*/
|
||||
public PermissionProxyAnnotationMethodInterceptor() {
|
||||
super( new PermissionProxyAnnotationHandler() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resolver
|
||||
* @since 1.1
|
||||
*/
|
||||
public PermissionProxyAnnotationMethodInterceptor(AnnotationResolver resolver) {
|
||||
super( new PermissionProxyAnnotationHandler(), resolver);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package com.diboot.shiro.bind.aop;
|
||||
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
|
||||
import com.diboot.shiro.bind.handler.PermissionWrapperAnnotationHandler;
|
||||
import org.apache.shiro.aop.AnnotationResolver;
|
||||
import org.apache.shiro.aop.MethodInvocation;
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
|
||||
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
|
||||
|
||||
/**
|
||||
* {@link RequiresPermissionsWrapper} 拦截器
|
||||
* @author : wee
|
||||
* @version : v2.0
|
||||
* @Date 2019-06-14 22:19
|
||||
*/
|
||||
public class PermissionWrapperAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
|
||||
/**
|
||||
* Default no-argument constructor that ensures this interceptor looks for
|
||||
* {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations in a method declaration.
|
||||
*/
|
||||
public PermissionWrapperAnnotationMethodInterceptor() {
|
||||
super( new PermissionWrapperAnnotationHandler() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resolver
|
||||
* @since 1.1
|
||||
*/
|
||||
public PermissionWrapperAnnotationMethodInterceptor(AnnotationResolver resolver) {
|
||||
super( new PermissionWrapperAnnotationHandler(), resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当使用RequiresPermissionsWrapper注解进行权限验证的时候,自动的去追加前缀
|
||||
* @param mi
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
@Override
|
||||
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
|
||||
try {
|
||||
|
||||
//默认是直接调用方法上注解,现在修改成 获取类和方法上的注解
|
||||
// ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));
|
||||
((PermissionWrapperAnnotationHandler)getHandler()).assertAuthorized(getResolver(), mi);
|
||||
}
|
||||
catch(AuthorizationException ae) {
|
||||
if (ae.getCause() == null) {
|
||||
ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
|
||||
}
|
||||
throw ae;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package com.diboot.shiro.bind.handler;
|
||||
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy;
|
||||
import org.apache.shiro.aop.AnnotationResolver;
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
|
||||
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
|
||||
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link RequiresPermissionsProxy} 助手类, 参考{@link PermissionAnnotationHandler}实现
|
||||
* @author : wee
|
||||
* @version : v2.0
|
||||
* @Date 2019-06-14 22:19
|
||||
*/
|
||||
public class PermissionProxyAnnotationHandler extends AuthorizingAnnotationHandler {
|
||||
|
||||
private final static String REQUIRES_PERMISSIONS_VALUE = "value";
|
||||
|
||||
private final static String REQUIRES_PERMISSIONS_LOGICAL = "logical";
|
||||
|
||||
private final static String JDK_MEMBER_VALUES = "memberValues";
|
||||
|
||||
/**
|
||||
* 标记服务的注解
|
||||
*/
|
||||
public PermissionProxyAnnotationHandler() {
|
||||
super(RequiresPermissionsProxy.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将{@link RequiresPermissionsProxy} 代理的内容赋值给{@link RequiresPermissions}
|
||||
*/
|
||||
@Override
|
||||
public void assertAuthorized(Annotation a) throws AuthorizationException {
|
||||
if (!(a instanceof RequiresPermissionsProxy)) {
|
||||
return;
|
||||
}
|
||||
RequiresPermissionsProxy rrAnnotation = (RequiresPermissionsProxy) a;
|
||||
try {
|
||||
//获取RequiresPermissionsProxy上的RequiresPermissions注解
|
||||
RequiresPermissions requiresPermissions = rrAnnotation.annotationType().getAnnotation(RequiresPermissions.class);
|
||||
|
||||
InvocationHandler invocationHandler = Proxy.getInvocationHandler(requiresPermissions);
|
||||
/* memberValues 为JDK中存储所有成员变量值的Map {@link AnnotationInvocationHandler#memberValues}*/
|
||||
Field jdkValue = invocationHandler.getClass().getDeclaredField(JDK_MEMBER_VALUES);
|
||||
jdkValue.setAccessible(true);
|
||||
/*获取RequiresPermissions对应的代理属性值*/
|
||||
Map<String, Object> memberValues = (Map<String, Object>) jdkValue.get(invocationHandler);
|
||||
/*动态设置RequiresPermissions注解的内容*/
|
||||
memberValues.put(REQUIRES_PERMISSIONS_VALUE, rrAnnotation.value());
|
||||
memberValues.put(REQUIRES_PERMISSIONS_LOGICAL, rrAnnotation.logical());
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package com.diboot.shiro.bind.handler;
|
||||
|
||||
import com.diboot.core.util.S;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.shiro.bind.annotation.PermissionsPrefix;
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
|
||||
import org.apache.shiro.aop.AnnotationResolver;
|
||||
import org.apache.shiro.aop.MethodInvocation;
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
|
||||
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link RequiresPermissionsWrapper} 助手类, 参考{@link PermissionAnnotationHandler}实现
|
||||
* @author : wee
|
||||
* @version : v2.0
|
||||
* @Date 2019-06-14 22:19
|
||||
*/
|
||||
public class PermissionWrapperAnnotationHandler extends AuthorizingAnnotationHandler {
|
||||
|
||||
private final static String REQUIRES_PERMISSIONS_VALUE = "value";
|
||||
|
||||
private final static String REQUIRES_PERMISSIONS_LOGICAL = "logical";
|
||||
|
||||
private final static String JDK_MEMBER_VALUES = "memberValues";
|
||||
|
||||
/**
|
||||
* 标记服务的注解
|
||||
*/
|
||||
public PermissionWrapperAnnotationHandler() {
|
||||
super(RequiresPermissionsWrapper.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the annotation {@link RequiresPermissions#value value}, from which the Permission will be constructed.
|
||||
*
|
||||
* @param a the RequiresPermissions annotation being inspected.
|
||||
* @return the annotation's <code>value</code>, from which the Permission will be constructed.
|
||||
*/
|
||||
protected String[] getAnnotationValue(Annotation a) {
|
||||
RequiresPermissionsWrapper rpAnnotation = (RequiresPermissionsWrapper) a;
|
||||
return rpAnnotation.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验注解{@link RequiresPermissionsWrapper}
|
||||
*/
|
||||
@Override
|
||||
public void assertAuthorized(Annotation a) throws AuthorizationException {
|
||||
if (!(a instanceof RequiresPermissionsWrapper)) {
|
||||
return;
|
||||
}
|
||||
RequiresPermissionsWrapper rppAnnotation = (RequiresPermissionsWrapper) a;
|
||||
String[] perms = getAnnotationValue(a);
|
||||
Subject subject = getSubject();
|
||||
|
||||
if (perms.length == 1) {
|
||||
subject.checkPermission(perms[0]);
|
||||
return;
|
||||
}
|
||||
if (Logical.AND.equals(rppAnnotation.logical())) {
|
||||
getSubject().checkPermissions(perms);
|
||||
return;
|
||||
}
|
||||
if (Logical.OR.equals(rppAnnotation.logical())) {
|
||||
boolean hasAtLeastOnePermission = false;
|
||||
for (String permission : perms) {
|
||||
if (getSubject().isPermitted(permission)) {
|
||||
hasAtLeastOnePermission = true;
|
||||
}
|
||||
}
|
||||
if (!hasAtLeastOnePermission) {
|
||||
getSubject().checkPermission(perms[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验注解{@link RequiresPermissionsWrapper}
|
||||
*/
|
||||
public void assertAuthorized(AnnotationResolver resolver, MethodInvocation mi) throws AuthorizationException {
|
||||
//如果方法上存在RequiresPermissionsWrapper注解,那么resolver.getAnnotation()获取的是RequiresPermissionsWrapper注解
|
||||
//优先从缓存读取
|
||||
RequiresPermissionsWrapper requiresPermissionsWrapper = (RequiresPermissionsWrapper)resolver.getAnnotation(mi, RequiresPermissionsWrapper.class);
|
||||
String prefix = "";
|
||||
if (V.notEmpty(requiresPermissionsWrapper.prefix())) {
|
||||
prefix = requiresPermissionsWrapper.prefix();
|
||||
} else {
|
||||
//如果自身不定义,查找前缀注解,存在则设置值
|
||||
PermissionsPrefix permissionsPrefix = (PermissionsPrefix)resolver.getAnnotation(mi, PermissionsPrefix.class);
|
||||
if (V.notEmpty(permissionsPrefix)) {
|
||||
prefix = permissionsPrefix.prefix();
|
||||
}
|
||||
}
|
||||
|
||||
String[] perms = getAnnotationValue(requiresPermissionsWrapper);
|
||||
Subject subject = getSubject();
|
||||
|
||||
String [] permsTemp = new String[perms.length];
|
||||
//前缀存在的时候,才做组装,其他情况不处理
|
||||
if (V.notEmpty(prefix)) {
|
||||
for (int i = 0; i < perms.length; i++) {
|
||||
permsTemp[i] = S.join(prefix, ":", perms[i]);
|
||||
}
|
||||
perms = permsTemp;
|
||||
}
|
||||
|
||||
if (perms.length == 1) {
|
||||
subject.checkPermission(perms[0]);
|
||||
return;
|
||||
}
|
||||
if (Logical.AND.equals(requiresPermissionsWrapper.logical())) {
|
||||
getSubject().checkPermissions(perms);
|
||||
return;
|
||||
}
|
||||
if (Logical.OR.equals(requiresPermissionsWrapper.logical())) {
|
||||
boolean hasAtLeastOnePermission = false;
|
||||
for (String permission : perms) {
|
||||
if (getSubject().isPermitted(permission)) {
|
||||
hasAtLeastOnePermission = true;
|
||||
}
|
||||
}
|
||||
if (!hasAtLeastOnePermission) {
|
||||
getSubject().checkPermission(perms[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态修改注解的值(不可用)
|
||||
* @param rppAnnotation
|
||||
*/
|
||||
@Deprecated
|
||||
private void proxy(RequiresPermissionsWrapper rppAnnotation) {
|
||||
try {
|
||||
//获取RequiresPermissionsProxy上的RequiresPermissions注解
|
||||
RequiresPermissions requiresPermissions = rppAnnotation.annotationType().getAnnotation(RequiresPermissions.class);
|
||||
|
||||
InvocationHandler invocationHandler = Proxy.getInvocationHandler(requiresPermissions);
|
||||
/* memberValues 为JDK中存储所有成员变量值的Map {@link AnnotationInvocationHandler#memberValues}*/
|
||||
Field jdkValue = invocationHandler.getClass().getDeclaredField(JDK_MEMBER_VALUES);
|
||||
jdkValue.setAccessible(true);
|
||||
/*获取RequiresPermissions对应的代理属性值*/
|
||||
Map<String, Object> memberValues = (Map<String, Object>) jdkValue.get(invocationHandler);
|
||||
/*动态设置RequiresPermissions注解的内容*/
|
||||
memberValues.put(REQUIRES_PERMISSIONS_VALUE, rppAnnotation.value());
|
||||
memberValues.put(REQUIRES_PERMISSIONS_LOGICAL, rppAnnotation.logical());
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,11 @@
|
|||
package com.diboot.shiro.config;
|
||||
|
||||
import com.diboot.shiro.bind.aop.PermissionProxyAnnotationMethodInterceptor;
|
||||
import com.diboot.shiro.bind.aop.CustomAuthorizationAttributeSourceAdvisor;
|
||||
import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter;
|
||||
import com.diboot.shiro.jwt.BaseJwtRealm;
|
||||
import org.apache.shiro.aop.AnnotationResolver;
|
||||
import org.apache.shiro.authz.aop.*;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.realm.Realm;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
import org.apache.shiro.spring.aop.SpringAnnotationResolver;
|
||||
import org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor;
|
||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
import org.apache.shiro.web.filter.authc.AnonymousFilter;
|
||||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
|
@ -22,9 +17,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.context.annotation.DependsOn;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/***
|
||||
|
@ -60,16 +53,7 @@ public class ShiroConfig {
|
|||
//用户访问未对其授权的资源时的错误提示页面
|
||||
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
|
||||
|
||||
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
|
||||
|
||||
filterChainDefinitionMap.put("/", "anon");
|
||||
filterChainDefinitionMap.put("/static/**", "anon");
|
||||
filterChainDefinitionMap.put("/auth/login", "anon");
|
||||
filterChainDefinitionMap.put("/error", "anon");
|
||||
filterChainDefinitionMap.put("/auth/logout", "logout");
|
||||
filterChainDefinitionMap.put("/**", "jwt");
|
||||
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinition());
|
||||
|
||||
// 设置过滤器
|
||||
Map<String, Filter> filters = new LinkedHashMap<>();
|
||||
|
@ -80,6 +64,22 @@ public class ShiroConfig {
|
|||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map filterChainDefinition(){
|
||||
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
|
||||
|
||||
filterChainDefinitionMap.put("/", "anon");
|
||||
filterChainDefinitionMap.put("/static/**", "anon");
|
||||
filterChainDefinitionMap.put("/auth/login", "anon");
|
||||
filterChainDefinitionMap.put("/auth/buildOAuthUrl", "anon");
|
||||
filterChainDefinitionMap.put("/auth/apply", "anon");
|
||||
filterChainDefinitionMap.put("/error", "anon");
|
||||
filterChainDefinitionMap.put("/auth/logout", "logout");
|
||||
filterChainDefinitionMap.put("/**", "jwt");
|
||||
|
||||
return filterChainDefinitionMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shiro生命周期处理器
|
||||
*/
|
||||
|
@ -101,20 +101,8 @@ public class ShiroConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
|
||||
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
|
||||
AopAllianceAnnotationsAuthorizingMethodInterceptor advice = (AopAllianceAnnotationsAuthorizingMethodInterceptor)authorizationAttributeSourceAdvisor.getAdvice();
|
||||
//重置拦截器,添加新的PermissionProxyAnnotationMethodInterceptor
|
||||
List<AuthorizingAnnotationMethodInterceptor> interceptors =new ArrayList<>(6);
|
||||
AnnotationResolver resolver = new SpringAnnotationResolver();
|
||||
interceptors.add(new PermissionProxyAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
|
||||
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));
|
||||
advice.setMethodInterceptors(interceptors);
|
||||
|
||||
public CustomAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
|
||||
CustomAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new CustomAuthorizationAttributeSourceAdvisor();
|
||||
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
|
||||
return authorizationAttributeSourceAdvisor;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import com.diboot.core.util.BeanUtils;
|
|||
import com.diboot.core.vo.JsonResult;
|
||||
import com.diboot.core.vo.Pagination;
|
||||
import com.diboot.core.vo.Status;
|
||||
import com.diboot.shiro.bind.annotation.PermissionsMenu;
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy;
|
||||
import com.diboot.shiro.bind.annotation.PermissionsPrefix;
|
||||
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
|
||||
import com.diboot.shiro.entity.Permission;
|
||||
import com.diboot.shiro.service.PermissionService;
|
||||
import com.diboot.shiro.vo.PermissionVO;
|
||||
|
@ -29,8 +29,8 @@ import java.util.List;
|
|||
* @version 2018/12/23
|
||||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
@PermissionsMenu(menuName = "权限", menuCode = "permission")
|
||||
@RestController
|
||||
@PermissionsPrefix(prefix = "permission", code = "permission", name = "权限")
|
||||
@RequestMapping("/permission")
|
||||
public class PermissionController extends BaseCrudRestController {
|
||||
|
||||
|
@ -39,6 +39,20 @@ public class PermissionController extends BaseCrudRestController {
|
|||
@Autowired
|
||||
private PermissionService permissionService;
|
||||
|
||||
/***
|
||||
* 查询Entity
|
||||
* @param id ID
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@RequiresPermissionsWrapper(prefix = "permissionSelf", value = {"get"}, name = "查看")
|
||||
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap)
|
||||
throws Exception{
|
||||
PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class);
|
||||
return new JsonResult(vo);
|
||||
}
|
||||
|
||||
/***
|
||||
* 查询ViewObject的分页数据 (此为非继承的自定义使用案例,更简化的调用父类案例请参考UserController)
|
||||
* <p>
|
||||
|
@ -48,8 +62,7 @@ public class PermissionController extends BaseCrudRestController {
|
|||
* @throws Exception
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@RequiresPermissionsProxy(value = {"permission:list"}, menuCode = "permission",
|
||||
menuName = "权限", permissionName = "列表")
|
||||
@RequiresPermissionsWrapper(value = {"list"}, name = "列表")
|
||||
public JsonResult getVOList(HttpServletRequest request) throws Exception{
|
||||
QueryWrapper<Permission> queryWrapper = buildQuery(request);
|
||||
// 构建分页
|
||||
|
@ -77,20 +90,6 @@ public class PermissionController extends BaseCrudRestController {
|
|||
return super.createEntity(entity, result, modelMap);
|
||||
}
|
||||
|
||||
/***
|
||||
* 查询Entity
|
||||
* @param id ID
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@RequiresPermissionsProxy(value = {"permission:get"}, menuCode = "permission", menuName = "权限", permissionName = "查看")
|
||||
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap)
|
||||
throws Exception{
|
||||
PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class);
|
||||
return new JsonResult(vo);
|
||||
}
|
||||
|
||||
/***
|
||||
* 更新Entity
|
||||
* @param id ID
|
||||
|
|
|
@ -56,6 +56,8 @@ public class BaseJwtAuthenticationFilter extends BasicHttpAuthenticationFilter {
|
|||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
|
||||
logger.debug("Token认证: onAccessDenied");
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
|
||||
JsonResult jsonResult = new JsonResult(Status.FAIL_INVALID_TOKEN);
|
||||
this.responseJson((HttpServletResponse) response, jsonResult);
|
||||
return false;
|
||||
|
|
|
@ -11,5 +11,6 @@ rootProject.name = 'diboot-v2'
|
|||
include 'diboot-core'
|
||||
include 'diboot-example'
|
||||
include 'diboot-shiro'
|
||||
include 'diboot-shiro-wx-mp'
|
||||
include 'diboot-docs'
|
||||
|
||||
|
|
Loading…
Reference in New Issue