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

This commit is contained in:
godchao 2019-06-22 12:08:39 +08:00
commit 9431d47e56
24 changed files with 805 additions and 31 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ application-*.properties
build/
out/
/.idea/
gradle/

View File

@ -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

View File

@ -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> {

View File

@ -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> {

View File

@ -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;
}
}
/***

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -0,0 +1,5 @@
__ _ __ __
____/ / (_) / /_ ____ ____ / /_
/ __ / / / / __ \ / __ \ / __ \ / __/
/ /_/ / / / / /_/ / / /_/ / / /_/ / / /_
\__,_/ /_/ /_.___/ \____/ \____/ \__/

View File

@ -0,0 +1,9 @@
/**
* @author : wee
* @Description: todo
* @Date 2019-05-17 19:15
*/
public class MainTest {
public static void main(String[] args) {
}
}

View File

@ -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 "";
}

View File

@ -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 "";
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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