Java的三大器
拦截器的作用
Java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。
功能:可以进行权限验证,审计日志等。
代码实现
拦截器配置类
package com.thk.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器的属性配置
*
*/
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
/**
* 重写addCorsMappings()解决跨域问题
* 配置:允许http请求进行跨域访问
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
//指哪些接口URL需要增加跨域设置
registry.addMapping("/**")
//.allowedOrigins("*")//指的是前端哪些域名被允许跨域
.allowedOriginPatterns("*")
//需要带cookie等凭证时,设置为true,就会把cookie的相关信息带上
.allowCredentials(true)
//指的是允许哪些方法
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
//cookie的失效时间,单位为秒(s),若设置为-1,则关闭浏览器就失效
.maxAge(3600);
}
/**
* 重写addInterceptors()实现拦截器
* 配置:要拦截的路径以及不拦截的路径
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册Interceptor拦截器(Interceptor这个类是我们自己写的拦截器类)
InterceptorRegistration registration = registry.addInterceptor(new Interceptor());
//addPathPatterns()方法添加需要拦截的路径
//所有路径都被拦截
registration.addPathPatterns("/**");
//excludePathPatterns()方法添加不拦截的路径
//添加不拦截路径
registration.excludePathPatterns(
//登录
"/login",
//退出登录
"/loginOut",
//获取验证码
"/getCode",
//发送短信
"/sendshortMessage",
//重置账号
"/unsealaccount",
//文件上传
"/uploadImg",
//html静态资源
"/**/*.html",
//js静态资源
"/**/*.js",
//css静态资源
"/**/*.css"
);
}
}
拦截器实现类
package com.thk.Interceptor;
import com.thk.controller.base.BaseController;
import com.thk.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器
*/
public class Interceptor extends BaseController implements HandlerInterceptor {
@Autowired
private RedisUtil redisUtil;
/**
* 在请求处理之前进行调用(Controller方法调用之前)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
//判断是否登录
boolean verifyPermissions = verifyPermissions(request);
//判断是否有权限
boolean competence = competence(request);
if (verifyPermissions && competence) {
return true;
}
//这里设置拦截以后重定向的页面,一般设置为登陆页面地址
response.sendRedirect(request.getContextPath() + "/error.html");
} catch (Exception e) {
e.printStackTrace();
}
return true;//如果设置为false时,被请求时,拦截器执行到此处将不会继续操作
//如果设置为true时,请求将会继续执行后面的操作
}
}
判断是否登录
1.从请求头中获取token
2.通过token从redis中获取当前登录对象(object)
3.判断object是否为空,如果为空就是未登录或者登录时间过期
/**
* 验证是否登录
*
* @param request
* @return
*/
public boolean verifyPermissions(HttpServletRequest request) {
String token = request.getHeader(Constant.TOKEN);
Object o = redisUtil.get(token);
if (o != null) {
return true;
}
return false;
}
判断是否有权限
1.从请求头中获取token
2.通过token从redis中获取当前登录对象(object)
3.通过对象查询数据库是否存在当前对象
4.获取登录对象的登录名 判断是否是(admin或者总经理)这两个账号拥有最高权限
5.判断是否被授权,(获取临时授权时设置的开始时间,结束时间,当前时间),
获取这个三个时间的时间戳,判断当前时间是否在开始时间和结束时间之间
如果开始时间和结束时间为空的话表示当前登录对象未被临时授权,会执行后面的 4,5,6,
如果有时间,但是当前时间不在这个时间段也会执行 4,5,6
如果有时间,并且当前时间在这个时间段之中,就会直接返回true
6.从请求头中获取当前接口的地址,
7.通过当前登录对象的id查询权限
8.判断当前登录对象的权限中是否包含当前接口的地址,如果包含,允许当前登录对象访问,如果不包含,则不允许当前登录人访问
/**
* 判断是否有权限
*
* @param request
* @return
*/
public boolean competence(HttpServletRequest request) {
//获取当前登录对象的全部信息
People people = peopleMapper.selectById(getUserId(request.getHeader(Constant.TOKEN)));
//管理员拥有全部权限
if (Constant.SUPER_ADMIN.equals(people.getUserName())) {
return true;
}
//判断是否被授权
//防止空指针
if (people.getStartDate() != null && people.getEndDate() != null) {
if (dateUtils.ifDate(people.getStartDate(), people.getEndDate(), new Date())) {
return true;
}
}
//从请求头中获取的地址
String requestURI = request.getRequestURI();
//通过角色id查询当前登陆对象的所有权限
List<Power> list = powerMapper.selectUrl(people.getRoleid());
ArrayList<String> stringList = new ArrayList<>();
if (!StringUtils.isEmpty(list)) {
list.forEach(r -> {
stringList.add(r.getUrl());
});
return lsitUtils.ifcontainString(stringList, requestURI);
}
return false;
}
测试
1.获取验证码
2.登录获取token
(eyJhbGciOiJIUzI1NiJ9eyJqdGkiOiIyMDFlNmY0MS1jM2NhLTRmODItYjAxNC01NWY3ZTU5ZmNkMzgiLCJpYXQiOjE2NTIwNzE1MTMsInN1YiI6InRoayIsImlzcyI6InN0YWZmIiwiZXhwIjoxNjUyMDczMzEzfQunBHUktwyuKpT6D0NDObrPmYGjQ_yU8-lNJ0NbAwHMI)
3.查询全部用户
总结:
避坑问题:
在拦截器中@autowired注入工具类,或者其他service,mapper会导致空指针异常
解决方案:
原因:
因为拦截器是在spring创建controller之前运行的,这时候这些controller,service,实体类等等这些东西spring并没有去创建,所以会注入失败,并且报空指针异常
解决方法
将这个拦截器类也交给spring来进行管理
1.写一个@bean 来创建拦截器类
2.在addInterceptors 方法引用中调用这个bean
3.修改后的代码
package com.thk.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器的属性配置
*
*/
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
/**
* 把Interceptor这个实现类交给spring进行管理-------避坑!!!
* @return
*/
@Bean
Interceptor getAdminInterceptor(){
return new Interceptor();
}
/**
* 重写addCorsMappings()解决跨域问题
* 配置:允许http请求进行跨域访问
*
* @param registry
* @Author 有梦想的肥宅
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
//指哪些接口URL需要增加跨域设置
registry.addMapping("/**")
//.allowedOrigins("*")//指的是前端哪些域名被允许跨域
.allowedOriginPatterns("*")
//需要带cookie等凭证时,设置为true,就会把cookie的相关信息带上
.allowCredentials(true)
//指的是允许哪些方法
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
//cookie的失效时间,单位为秒(s),若设置为-1,则关闭浏览器就失效
.maxAge(3600);
}
/**
* 重写addInterceptors()实现拦截器
* 配置:要拦截的路径以及不拦截的路径
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册Interceptor拦截器(Interceptor这个类是我们自己写的拦截器类)
InterceptorRegistration registration = registry.addInterceptor(getAdminInterceptor());
//addPathPatterns()方法添加需要拦截的路径
//所有路径都被拦截
registration.addPathPatterns("/**");
//excludePathPatterns()方法添加不拦截的路径
//添加不拦截路径
registration.excludePathPatterns(
//登录
"/login",
//退出登录
"/loginOut",
//获取验证码
"/getCode",
//发送短信
"/sendshortMessage",
//重置账号
"/unsealaccount",
//文件上传
"/uploadImg",
//html静态资源
"/**/*.html",
//js静态资源
"/**/*.js",
//css静态资源
"/**/*.css"
);
}
}