Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
9431d47e56
|
@ -8,3 +8,4 @@ application-*.properties
|
|||
build/
|
||||
out/
|
||||
/.idea/
|
||||
gradle/
|
|
@ -35,7 +35,12 @@ subprojects {
|
|||
lombokVersion = "1.18.8"
|
||||
}
|
||||
dependencies {
|
||||
//gradle 4.7版本以上,使用如下方式
|
||||
// annotationProcessor("org.projectlombok:lombok:$lombokVersion")
|
||||
// compileOnly("org.projectlombok:lombok:$lombokVersion")
|
||||
//gradle 4.7版本以下,使用如下方式
|
||||
compileOnly("org.projectlombok:lombok:$lombokVersion")
|
||||
|
||||
compile("javax.servlet:javax.servlet-api:4.0.1")
|
||||
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
|
||||
// Mybatis
|
||||
|
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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,17 @@
|
|||
package diboot.core.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class StartupApplication extends SpringBootServletInitializer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StartupApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package diboot.core.test.config;
|
||||
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.diboot.core.util.D;
|
||||
import com.diboot.core.util.DateConverter;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/***
|
||||
* Spring配置文件
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/6/10
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableTransactionManagement(proxyTargetClass=true)
|
||||
@ComponentScan(basePackages={"com.diboot"})
|
||||
@MapperScan({"com.diboot.**.mapper"})
|
||||
public class SpringMvcConfig implements WebMvcConfigurer{
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class);
|
||||
|
||||
/**
|
||||
* JSON转换组件替换为fastJson
|
||||
*/
|
||||
@Bean
|
||||
public HttpMessageConverters fastJsonHttpMessageConverters() {
|
||||
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
||||
//处理中文乱码问题
|
||||
List<MediaType> fastMediaTypes = new ArrayList<>();
|
||||
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
|
||||
converter.setSupportedMediaTypes(fastMediaTypes);
|
||||
// 配置转换格式
|
||||
FastJsonConfig fastJsonConfig = new FastJsonConfig();
|
||||
// 设置fastjson的序列化参数:禁用循环依赖检测,数据兼容浏览器端(避免JS端Long精度丢失问题)
|
||||
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
|
||||
SerializerFeature.BrowserCompatible);
|
||||
fastJsonConfig.setDateFormat(D.FORMAT_DATETIME_Y4MDHM);
|
||||
converter.setFastJsonConfig(fastJsonConfig);
|
||||
|
||||
HttpMessageConverter<?> httpMsgConverter = converter;
|
||||
return new HttpMessageConverters(httpMsgConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
registry.addConverter(new DateConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mybatis-plus分页插件
|
||||
*/
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
|
||||
return paginationInterceptor;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package diboot.core.test.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.diboot.core.config.BaseConfig;
|
||||
import com.diboot.core.entity.Metadata;
|
||||
import com.diboot.core.service.MetadataService;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.Pagination;
|
||||
import diboot.core.test.StartupApplication;
|
||||
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;
|
||||
|
||||
/**
|
||||
* BaseService接口实现测试 (需先执行example中的初始化SQL)
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/06/15
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = {StartupApplication.class})
|
||||
public class BaseServiceTest {
|
||||
|
||||
@Autowired
|
||||
MetadataService metadataService;
|
||||
|
||||
@Test
|
||||
public void testGet(){
|
||||
// 查询总数
|
||||
int count = metadataService.getEntityListCount(null);
|
||||
Assert.assertTrue(count > 0);
|
||||
// 查询list
|
||||
List<Metadata> metadataList = metadataService.getEntityList(null);
|
||||
Assert.assertTrue(V.notEmpty(metadataList));
|
||||
Assert.assertTrue(metadataList.size() == count);
|
||||
// 第一页数据
|
||||
List<Metadata> pageList = metadataService.getEntityList(null, new Pagination());
|
||||
Assert.assertTrue(pageList.size() > 0 && pageList.size() <= BaseConfig.getPageSize());
|
||||
|
||||
// 查询单个记录
|
||||
Long id = metadataList.get(0).getId();
|
||||
Metadata first = metadataService.getEntity(id);
|
||||
Assert.assertTrue(first != null);
|
||||
|
||||
// 只查询第一条记录对应type类型的
|
||||
LambdaQueryWrapper<Metadata> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Metadata::getType, first.getType());
|
||||
metadataList = metadataService.getEntityList(queryWrapper);
|
||||
Assert.assertTrue(V.notEmpty(metadataList));
|
||||
// 结果type值一致
|
||||
metadataList.stream().forEach( m -> {
|
||||
Assert.assertTrue(m.getType().equals(first.getType()));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUpdateAndDelete(){
|
||||
// 创建
|
||||
String TYPE = "ID_TYPE";
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.setType(TYPE);
|
||||
metadata.setItemName("证件品类");
|
||||
metadata.setParentId(0L);
|
||||
metadataService.createEntity(metadata);
|
||||
|
||||
// 查询是否创建成功
|
||||
LambdaQueryWrapper<Metadata> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Metadata::getType, TYPE);
|
||||
List<Metadata> metadataList = metadataService.getEntityList(queryWrapper);
|
||||
Assert.assertTrue(V.notEmpty(metadataList));
|
||||
|
||||
// 更新
|
||||
metadata.setItemName("证件类型");
|
||||
metadataService.updateEntity(metadata);
|
||||
Metadata metadata2 = metadataService.getEntity(metadata.getId());
|
||||
Assert.assertTrue(metadata2.getItemName().equals(metadata.getItemName()));
|
||||
|
||||
// 删除
|
||||
metadataService.deleteEntity(metadata.getId());
|
||||
metadata2 = metadataService.getEntity(metadata.getId());
|
||||
Assert.assertTrue(metadata2 == null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
# spring config
|
||||
spring.devtools.restart.enabled=true
|
||||
|
||||
#datasource config
|
||||
spring.datasource.url=jdbc:mysql://localhost:3306/diboot_example?characterEncoding=utf8&serverTimezone=GMT%2B8
|
||||
spring.datasource.username=diboot
|
||||
spring.datasource.password=123456
|
||||
spring.datasource.hikari.maximum-pool-size=5
|
||||
spring.datasource.hikari.data-source-properties.useInformationSchema=true
|
||||
spring.datasource.hikari.data-source-properties.nullCatalogMeansCurrent=true
|
||||
# 数据库驱动
|
||||
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
# mybatis配置
|
||||
#mybatis.configuration.cache-enabled=false
|
||||
#mybatis.configuration.lazy-loading-enabled=true
|
||||
#mybatis.configuration.map-underscore-to-camel-case=true
|
||||
#mybatis.configuration.multiple-result-sets-enabled=false
|
||||
#mybatis.configuration.use-generated-keys=true
|
||||
#mybatis.configuration.auto-mapping-behavior=full
|
||||
#mybatis.configuration.default-statement-timeout=60
|
||||
#mybatis.configuration.log-impl=org.apache.ibatis.logging.log4j2.Log4j2Impl
|
||||
|
||||
# logging config
|
||||
logging.pattern.console=%clr{%d{MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${PID}}{faint} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx
|
||||
logging.level.root=info
|
||||
logging.level.org.apache=info
|
||||
logging.level.org.hibernate.validator=info
|
||||
logging.level.org.springframework=info
|
||||
logging.level.com.zaxxer.hikari=info
|
||||
logging.level.com.diboot=debug
|
||||
logging.level.org.mybatis=debug
|
|
@ -0,0 +1,5 @@
|
|||
__ _ __ __
|
||||
____/ / (_) / /_ ____ ____ / /_
|
||||
/ __ / / / / __ \ / __ \ / __ \ / __/
|
||||
/ /_/ / / / / /_/ / / /_/ / / /_/ / / /_
|
||||
\__,_/ /_/ /_.___/ \____/ \____/ \__/
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* @author : wee
|
||||
* @Description: todo
|
||||
* @Date 2019-05-17 19:15
|
||||
*/
|
||||
public class MainTest {
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
}
|
|
@ -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 "";
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.diboot.shiro.bind.annotation;
|
||||
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 注解{@link RequiresPermissions}的包装,增加权限描述等字段
|
||||
* @author : wee
|
||||
* @version v2.0
|
||||
* @Date 2019-06-14 17:50
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
public @interface RequiresPermissionsWrapper {
|
||||
|
||||
/**
|
||||
* 包装 {@link RequiresPermissions#value()}
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* 包装 {@link RequiresPermissions#logical()}
|
||||
*/
|
||||
Logical logical() default Logical.AND;
|
||||
|
||||
/**
|
||||
* 权限名称
|
||||
* @return
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,11 +1,11 @@
|
|||
package com.diboot.shiro.config;
|
||||
|
||||
import com.diboot.shiro.bind.aop.CustomAuthorizationAttributeSourceAdvisor;
|
||||
import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter;
|
||||
import com.diboot.shiro.jwt.BaseJwtRealm;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.realm.Realm;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
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;
|
||||
|
@ -101,8 +101,8 @@ public class ShiroConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
|
||||
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
|
||||
public CustomAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
|
||||
CustomAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new CustomAuthorizationAttributeSourceAdvisor();
|
||||
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
|
||||
return authorizationAttributeSourceAdvisor;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||
import com.diboot.core.controller.BaseCrudRestController;
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.core.util.BeanUtils;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.JsonResult;
|
||||
import com.diboot.core.vo.Pagination;
|
||||
import com.diboot.core.vo.Status;
|
||||
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,6 +30,7 @@ import java.util.List;
|
|||
* Copyright © www.dibo.ltd
|
||||
*/
|
||||
@RestController
|
||||
@PermissionsPrefix(prefix = "permission", code = "permission", name = "权限")
|
||||
@RequestMapping("/permission")
|
||||
public class PermissionController extends BaseCrudRestController {
|
||||
|
||||
|
@ -37,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>
|
||||
|
@ -45,8 +61,8 @@ public class PermissionController extends BaseCrudRestController {
|
|||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequiresPermissions("permission:list")
|
||||
@GetMapping("/list")
|
||||
@RequiresPermissionsWrapper(value = {"list"}, name = "列表")
|
||||
public JsonResult getVOList(HttpServletRequest request) throws Exception{
|
||||
QueryWrapper<Permission> queryWrapper = buildQuery(request);
|
||||
// 构建分页
|
||||
|
@ -74,20 +90,6 @@ public class PermissionController extends BaseCrudRestController {
|
|||
return super.createEntity(entity, result, modelMap);
|
||||
}
|
||||
|
||||
/***
|
||||
* 查询Entity
|
||||
* @param id ID
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequiresPermissions("permission:get")
|
||||
@GetMapping("/{id}")
|
||||
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;
|
||||
|
|
|
@ -58,7 +58,7 @@ public class UsernamePasswordAuthWayServiceImpl implements AuthWayService {
|
|||
|
||||
@Override
|
||||
public boolean isPasswordMatch() {
|
||||
String password = new String(token.getPassword());
|
||||
String password = token.getPassword();
|
||||
|
||||
// 构建查询条件
|
||||
QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
|
||||
|
|
Binary file not shown.
|
@ -1,5 +0,0 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
Loading…
Reference in New Issue