admin管理员组文章数量:1641852
Spring---WebMvcConfigurer
- 简介
- WebMvcConfigurer 配合HandlerInterceptor 拦截器配置校验服务
- Spring拦截器分类
- HandlerInterceptor
- 例子
- 简单描述@Order()注解
- @Order的注解源码解读
- Ordered接口类
- 结论
- 总结
简介
WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口
WebMvcConfigurer 配合HandlerInterceptor 拦截器配置校验服务
Spring拦截器分类
spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
HandlerInterceptor
HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。
HandlerInterceptor拦截的是请求地址,所以针对请求地址做一些验证、预处理等操作比较合适。当你需要统计请求的响应时间时MethodInterceptor将不太容易做到,因为它可能跨越很多方法或者只涉及到已经定义好的方法中一部分代码。
实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。
看下UML:
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
public HandlerInterceptorAdapter() {
}
//在业务处理器处理请求之前被调用
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//在业务处理器处理请求完成之后,生成视图之前执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
// 在DispatcherServlet完全处理完请求之后被调用,可用于清理资源
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
}
}
执行顺序如下:
例子
public class SecurityConfig implements WebMvcConfigurer {
@Bean
public SecurityInterceptor securityInterceptor() {
return new SecurityInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(securityInterceptor()).addPathPatterns("/**").excludePathPatterns("/js/**", "/css/**","/images/**");
}
}
@Order(0)
public class SecurityInterceptor implements AsyncHandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(SecurityInterceptor.class);
@Autowired
private TblResourceService resourceService;
@Autowired
private CustomConfig customConfig;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String requestURI = request.getRequestURI();
Map<String, List<ResourceUrlVO>> allResourceUrls = resourceService.getAllResourceUrls();
if (allResourceUrls == null)
{
errorPrint(response, request, requestURI, InfoCodeEnum.ERROR_CODE.getCode(), "没有配置任何接口!");
return false;
}
if (MapUtil.isEmpty(allResourceUrls)) {
for (int i=0; i<5; i++)
{
allResourceUrls = resourceService.getAllResourceUrls();
if (!MapUtil.isEmpty(allResourceUrls))
{
break;
}
if (i == 5)
{
errorPrint(response, request, requestURI, InfoCodeEnum.ERROR_CODE.getCode(), "请求地址非法!");
return false;
}
}
}
List<ResourceUrlVO> resourceUrlVOS = allResourceUrls.get(requestURI);
if (CollUtil.isEmpty(resourceUrlVOS)) {
for (int i=0; i<5; i++)
{
resourceUrlVOS = allResourceUrls.get(requestURI);
if (!CollUtil.isEmpty(resourceUrlVOS))
{
break;
}
if (i == 5)
{
errorPrint(response, request, requestURI, "999888", "请求地址非法!");
return false;
}
}
}
if(resourceUrlVOS==null){
errorPrint(response, request, requestURI, "999000", "请求地址非法!");
return false;
}
List<String> resourceIds = resourceUrlVOS.stream().map(urlVO -> urlVO.getResourceId() + "").collect(Collectors.toList());
String cookieValue = CookieUtil.getCookieValue(request, customConfig.getCookieName());
boolean present = resourceIds.stream().anyMatch(id -> !id.equals("-1"));
// 不存在说明只含有免登录的Url,直接放行
if (!present) {
if (StrUtil.isNotBlank(cookieValue)) {
Claims claims = JwtUtil.verifyJWT(cookieValue);
Object userIdStr;
if (claims != null && (userIdStr = claims.get(LoginParamsEnum.USERID.getName())) != null) {
request.setAttribute(LoginParamsEnum.USERID.getName(), Long.parseLong(userIdStr.toString()));
request.setAttribute(LoginParamsEnum.NICK_NAME.getName(), claims.get(LoginParamsEnum.NICK_NAME.getName()));
}
}
return true;
}
boolean match = resourceIds.stream().anyMatch(id -> !id.equals("0"));
// 不存在说明只含有只需要登录的Url,直接校验token是否存在,存在就放行
if (!match) {
if (StrUtil.isBlank(cookieValue)) {
errorPrint(response, request, requestURI, InfoCodeEnum.NOT_LOGIN_CODE.getCode(), "登录失效,请重新登录!");
return false;
}
Claims claims = JwtUtil.verifyJWT(cookieValue);
Object userIdStr;
if (claims != null && (userIdStr = claims.get(LoginParamsEnum.USERID.getName())) != null) {
request.setAttribute(LoginParamsEnum.USERID.getName(), Long.parseLong(userIdStr.toString()));
request.setAttribute(LoginParamsEnum.NICK_NAME.getName(), claims.get(LoginParamsEnum.NICK_NAME.getName()));
}
return true;
}
// 需要有对应权限才允许放行
if (StrUtil.isBlank(cookieValue)) {
errorPrint(response, request, requestURI, InfoCodeEnum.NOT_LOGIN_CODE.getCode(), "登录失效,请重新登录!");
return false;
}
// 已登录
// 超管需要直接放行
Claims claims = JwtUtil.verifyJWT(cookieValue);
Object userIdObj;
if (claims == null || (userIdObj = claims.get(LoginParamsEnum.USERID.getName())) == null) {
errorPrint(response, request, requestURI, InfoCodeEnum.NOT_LOGIN_CODE.getCode(), "登录失效,请重新登录!");
return false;
}
Long userId = Long.parseLong(userIdObj.toString());
List<TblRole> allRoles = resourceService.getAllRoles(userId);
boolean adminMatch = allRoles.stream().anyMatch(role -> role.getFlag().equals(1L));
if (adminMatch) {
Object userIdStr;
if (claims != null && (userIdStr = claims.get(LoginParamsEnum.USERID.getName())) != null) {
request.setAttribute(LoginParamsEnum.USERID.getName(), Long.parseLong(userIdStr.toString()));
request.setAttribute(LoginParamsEnum.NICK_NAME.getName(), claims.get(LoginParamsEnum.NICK_NAME.getName()));
}
return true;
}
// 判断用户资源是否包含当前对应的resourceId
List<TblResource> allResources = resourceService.getAllResources(userId);
boolean anyMatch = allResources.stream().anyMatch(resource -> resourceIds.contains(resource.getId().toString()));
if (anyMatch) {
Object userIdStr;
if (claims != null && (userIdStr = claims.get(LoginParamsEnum.USERID.getName())) != null) {
request.setAttribute(LoginParamsEnum.USERID.getName(), Long.parseLong(userIdStr.toString()));
request.setAttribute(LoginParamsEnum.NICK_NAME.getName(), claims.get(LoginParamsEnum.NICK_NAME.getName()));
}
return true;
}
errorPrint(response, request, requestURI, null, "当前用户权限不足!");
return false;
}
private void errorPrint(HttpServletResponse response, HttpServletRequest request, String location, String code,
String message) {
PrintWriter out = null;
try {
// response.reset(); 注释掉此代码才可以清空掉Cookies信息
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
out = response.getWriter();
log.error("user-agent:" + request.getHeader("user-agent"));
log.error("身份验证失败的位置:" + location);
out.print(JSONObject.toJSONString(ActionResult.getErrorResult(code, message)));
log.error("错误信息: {}", JSONObject.toJSONString(ActionResult.getErrorResult(code, message)));
} catch (Exception ex) {
log.error("返回写入异常", ex);
if (out != null) {
out.close();
}
}
}
简单描述@Order()注解
注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响;
@Order的注解源码解读
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
/**
* 默认是最低优先级,值越小优先级越高
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}
注解可以作用在类(接口、枚举)、方法、字段声明(包括枚举常量);
注解有一个int类型的参数,可以不传,默认是最低优先级;
通过常量类的值我们可以推测参数值越小优先级越高
Ordered接口类
package org.springframework.core;
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;
int getOrder();
}
结论
1、如果多个拦截器,执行顺序:preHandle() :123 ,postHandle():321,afterCompletion():321
2、preHandle() 返回false,不会往下执行
3、preHandle()返回true的拦截器,必然会执行他的afterCompletion();
4、如果preHandle()都返回true,有一个拦截器的postHandle()抛出异常,则后面拦截器的postHandle()不会执行。(注意:写代码时要注意)
总结
参考相关链接:
WebMvcConfigurer讲解
相关例子参考
@Order注解详解
本文标签: SpringWebMvcConfigurer
版权声明:本文标题:《Spring篇》---WebMvcConfigurer 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1729329501a1196204.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论