持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
之前已经将Shiro框架的AuthenticatingFilter、AuthenticatingFilter和AuthorizingRealm的实现类成功创建了,但是这些都只是普通的类,只有将他们配置到ShiroConfig中,才能在Shiro框架中起作用。
编写Shiro框架的配置类ShiroConfig
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(oAuth2Realm);
securityManager.setRememberMeManager(null);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager,OAuth2Filter oAuth2Filter) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//oauth过滤
Map<String, Filter> filters = new HashMap<>();
filters.put("oauth2", oAuth2Filter);
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/webjars/**", "anon");
filterMap.put("/druid/**", "anon");
filterMap.put("/app/**", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/swagger/**", "anon");
filterMap.put("/v2/api-docs", "anon");
filterMap.put("/swagger-ui.html", "anon");
filterMap.put("/swagger-resources/**", "anon");
filterMap.put("/captcha.jpg", "anon");
filterMap.put("/user/register", "anon");
filterMap.put("/user/login", "anon");
filterMap.put("/test/**", "anon");
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
直到这里,Shiro框架的部分就算编写完成了,之前在编写拦截器类(AuthenticatingFilter)的时候,已经实现了,当token过期但是redis中的token没有过期时,自动往ThreadLocalToken中添加刷新后的token。此时就可以通过AOP切面类,查询ThreadLocalToken中是否有token。如果有,将token加入到返回对象中,前端将最新的token添加到本地缓存,就可以实现token的自动刷新。
AOP切面类在返回的R对象中添加更新后的令牌
在实现自动刷新的同时,也可以加入请求与返回参数以及用时的打印,敏感字段也可以排除,也可以实现将过慢的接口耗时打印到文档中,或者实现报警邮箱等功能也可以。
@Slf4j
@Aspect
@Component
public class TokenAspect {
@Autowired
private ThreadLocalToken threadLocalToken;
@Pointcut("execution(public * com.taluohui.wx.controller.*.*(..)))")
public void aspect() {
}
@Around("aspect()")
public Object around(ProceedingJoinPoint point) throws Throwable {
R r = (R) point.proceed(); //方法执行结果
String token = threadLocalToken.getToken();
//如果ThreadLocal中存在Token,说明是更新的Token
if (token != null) {
r.put("token", token); //往响应中放置Token
threadLocalToken.clear();
}
return r;
}
@Before("aspect()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
MDC.put("LOG_ID", String.valueOf(UUID.randomUUID()));
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Signature signature = joinPoint.getSignature();
String name = signature.getName();
// 打印请求信息
log.info("------------- 开始 -------------");
log.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
log.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
log.info("远程地址: {}", request.getRemoteAddr());
// 打印请求参数
Object[] args = joinPoint.getArgs();
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest
|| args[i] instanceof ServletResponse
|| args[i] instanceof MultipartFile) {
continue;
}
arguments[i] = args[i];
}
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "file"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
log.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
}
@Around("aspect()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "file"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
log.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));
log.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
return result;
}
}
需要添加fastJson的依赖
最新的依赖需要修改代码,并没办法直接使用,如果嫌麻烦,可以和本版本保持一致。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>