做权限控制的时候,需要对Controller层且用Swagger标签标注的方法进行扫描,并将权限列表存入Redis缓存中,已达到对用户权限进行控制的目的。
1、首先通过Spring的RequestMappingHandlerMapping类来进行扫描
package com.grid.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* 获取当前Mapping映射
*/
@Configuration
public class RequestMappingHandlerConfig {
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
return mapping;
}
}
2、扫描Swagger注解中的内容,并存入Redis缓存中,这里我用的是RedisSession中自定义的RedisTemplate。
package com.grid.config;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.grid.common.EnableAccessControl;
import com.grid.common.Enum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class EnableAccessConfig {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RequestMappingHandlerConfig requestMappingHandlerConfig;
/**
* 扫描URL
*/
@Scheduled(fixedRate = 120000,initialDelay=1000)
//@CachePut(value = "AccessRoleMapper")
public HashMap>> detectHandlerMethods() {
HashMap>> appMap=Maps.newHashMap();
if(redisTemplate.opsForValue().get("AccessRoleMapper")!=null){
appMap=(HashMap>>)redisTemplate.opsForValue().get("AccessRoleMapper");
}
//先获取redis内容 ,将多个项目的redis整合到一块
List> urlList = Lists.newArrayList();
final RequestMappingHandlerMapping requestMappingHandlerMapping = requestMappingHandlerConfig.requestMappingHandlerMapping();
Map map = requestMappingHandlerMapping.getHandlerMethods();
for (Map.Entry m : map.entrySet()) {
HashMap hashMap = Maps.newHashMap();
RequestMappingInfo info = m.getKey();
HandlerMethod method = m.getValue();
PatternsRequestCondition p = info.getPatternsCondition();
//有@EnableAccessControl的注解,才加入hashMap
if (checkEnableAccessControl(method)) {
for (String url : p.getPatterns()) {
//url地址
hashMap.put("url", url);
}
// 类名
hashMap.put("className", method.getMethod().getDeclaringClass().getName());
//类描述
hashMap.put("controllerDescription", getControllerDescription(method.getMethod().getDeclaringClass()));
// 方法名
hashMap.put("method", method.getMethod().getName());
// 方法描述
hashMap.put("methodDescription", getMethodDescription(method));
urlList.add(hashMap);
}
}
appMap.put("GRID",urlList);
redisTemplate.opsForValue().set("AccessRoleMapper",appMap);
return appMap;
}
/**
* 获取Controller描述信息
* @param c
*/
private static String getControllerDescription(Class> c) {
Api annotation = c.getAnnotation(Api.class);
if (annotation != null) {
return annotation.description();
}
return "";
}
/**
* 获取方法描述信息
* @param method
*/
private static String getMethodDescription(HandlerMethod method) {
ApiOperation annotation = method.getMethodAnnotation(ApiOperation.class);
if (annotation != null) {
return annotation.value();
}
return "";
}
/**
* 判断当前 method 是否包含@EnableAccessControl
* @param method
* @return
*/
private static boolean checkEnableAccessControl(HandlerMethod method){
EnableAccessControl annotation = method.getMethodAnnotation(EnableAccessControl.class);
if (annotation != null) {
return true;
}
return false;
}
}
3、因为默认扫描的话,会将所有的Mapping进行扫描,这里我自定了一个@EnableAccessControl注解,只识别具有该注解的方法。
package com.grid.common;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface EnableAccessControl{
String value() default '';
}
4、下面的步骤就为非必须的啦,因为我用的RedisTemplate,所以将这部分的代码拷出来。
package com.grid.config;
import com.grid.common.TedisCacheManager;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import java.time.Duration;
@Configuration
@EnableRedisHttpSession
@EnableCaching
public class RedisSessionConfig extends CachingConfigurerSupport {
private final RedisConnectionFactory redisConnectionFactory;
RedisSessionConfig(RedisConnectionFactory redisConnectionFactory) {
this.redisConnectionFactory = redisConnectionFactory;
}
/**
* 设置 redisTemplate 序列化方式
*
* @param factory
* @return
*/
@Bean
public RedisTemplate
5、如果不用@CachePut、@Cacheable、@Cachevict等注解的话,那么CacheManager也就不需要用到。如果需要注解的话,那么就得用到这部分的内容。
package com.grid.common;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ReflectionUtils;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.Callable;
public class TedisCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean {
private ApplicationContext applicationContext;
private static Logger log = LogManager.getLogger("TedisCacheManager");
private Map initialCacheConfiguration = new LinkedHashMap<>();
/**
* key serializer
*/
public static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();
/**
* value serializer
*/
public static final GenericJackson2JsonRedisSerializer JACKSON_SERIALIZER = new GenericJackson2JsonRedisSerializer();
/**
* key serializer pair
*/
public static final RedisSerializationContext.SerializationPair STRING_PAIR = RedisSerializationContext
.SerializationPair.fromSerializer(STRING_SERIALIZER);
/**
* value serializer pair
*/
public static final RedisSerializationContext.SerializationPair JACKSON__PAIR = RedisSerializationContext
.SerializationPair.fromSerializer(JACKSON_SERIALIZER);
public TedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
@Override
public Cache getCache(String name) {
Cache cache = super.getCache(name);
return new RedisCacheWrapper(cache);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() {
String[] beanNames = applicationContext.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
final Class clazz = applicationContext.getType(beanName);
add(clazz);
}
super.afterPropertiesSet();
}
@Override
protected Collection loadCaches() {
List caches = new LinkedList<>();
for (Map.Entry entry : initialCacheConfiguration.entrySet()) {
caches.add(super.createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}
private void add(final Class clazz) {
ReflectionUtils.doWithMethods(clazz, method -> {
ReflectionUtils.makeAccessible(method);
CacheExpire cacheExpire = AnnotationUtils.findAnnotation(method, CacheExpire.class);
if (cacheExpire == null) {
return;
}
Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
if (cacheable != null) {
add(cacheable.cacheNames(), cacheExpire);
return;
}
Caching caching = AnnotationUtils.findAnnotation(method, Caching.class);
if (caching != null) {
Cacheable[] cs = caching.cacheable();
if (cs.length > 0) {
for (Cacheable c : cs) {
if (cacheExpire != null && c != null) {
add(c.cacheNames(), cacheExpire);
}
}
}
} else {
CacheConfig cacheConfig = AnnotationUtils.findAnnotation(clazz, CacheConfig.class);
if (cacheConfig != null) {
add(cacheConfig.cacheNames(), cacheExpire);
}
}
}, method -> null != AnnotationUtils.findAnnotation(method, CacheExpire.class));
}
private void add(String[] cacheNames, CacheExpire cacheExpire) {
for (String cacheName : cacheNames) {
if (cacheName == null || "".equals(cacheName.trim())) {
continue;
}
long expire = cacheExpire.expire();
log.info("cacheName: {}, expire: {}", cacheName, expire);
if (expire >= 0) {
// 缓存配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(expire))
.disableCachingNullValues()
// .prefixKeysWith(cacheName)
.serializeKeysWith(STRING_PAIR)
.serializeValuesWith(JACKSON__PAIR);
initialCacheConfiguration.put(cacheName, config);
} else {
log.warn("{} use default expiration.", cacheName);
}
}
}
protected static class RedisCacheWrapper implements Cache {
private final Cache cache;
RedisCacheWrapper(Cache cache) {
this.cache = cache;
}
@Override
public String getName() {
// log.info("name: {}", cache.getName());
try {
return cache.getName();
} catch (Exception e) {
log.error("getName ---> errmsg: {}", e.getMessage(), e);
return null;
}
}
@Override
public Object getNativeCache() {
// log.info("nativeCache: {}", cache.getNativeCache());
try {
return cache.getNativeCache();
} catch (Exception e) {
log.error("getNativeCache ---> errmsg: {}", e.getMessage(), e);
return null;
}
}
@Override
public ValueWrapper get(Object o) {
// log.info("get ---> o: {}", o);
try {
return cache.get(o);
} catch (Exception e) {
log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
return null;
}
}
@Override
public T get(Object o, Class aClass) {
// log.info("get ---> o: {}, clazz: {}", o, aClass);
try {
return cache.get(o, aClass);
} catch (Exception e) {
log.error("get ---> o: {}, clazz: {}, errmsg: {}", o, aClass, e.getMessage(), e);
return null;
}
}
@Override
public T get(Object o, Callable callable) {
// log.info("get ---> o: {}", o);
try {
return cache.get(o, callable);
} catch (Exception e) {
log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
return null;
}
}
@Override
public void put(Object o, Object o1) {
// log.info("put ---> o: {}, o1: {}", o, o1);
try {
cache.put(o, o1);
} catch (Exception e) {
log.error("put ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
}
}
@Override
public ValueWrapper putIfAbsent(Object o, Object o1) {
// log.info("putIfAbsent ---> o: {}, o1: {}", o, o1);
try {
return cache.putIfAbsent(o, o1);
} catch (Exception e) {
log.error("putIfAbsent ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
return null;
}
}
@Override
public void evict(Object o) {
// log.info("evict ---> o: {}", o);
try {
cache.evict(o);
} catch (Exception e) {
log.error("evict ---> o: {}, errmsg: {}", o, e.getMessage(), e);
}
}
@Override
public void clear() {
// log.info("clear");
try {
cache.clear();
} catch (Exception e) {
log.error("clear ---> errmsg: {}", e.getMessage(), e);
}
}
}
}
package com.grid.common;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheExpire {
/**
* expire time, default 60s
*/
@AliasFor("expire")
long value() default 60L;
/**
* expire time, default 60s
*/
@AliasFor("value")
long expire() default 60L;
}