上一章做了登录注册功能,但是只是简单的检验,成功就跳转到成功页面,失败就跳转到失败页面。如果不小心成功页面的URL别人知道了,那岂不是可以直接跳转登录注册,直接访问系统资源。所以我们需要对某些页面进行权限拦截。
功能分析:
1、新建一个类:记录当前用户的个人信息
2、我们需要实现自己的拦截器:
springboot中实现拦截器的两种方式,分别为:实现HandlerInterceptor接口和使用servlet的filter拦截器。
3、在拦截器中队当前用户进行判断:(1)是否为登录状态 (2)当前用户是否有权限能访问该资源
4、把拦截器交给spring管理,注意拦截器先后顺序
一、记录当前用户的个人信息
public class HostHolder {
private static ThreadLocal<User> users = new ThreadLocal<User>();
public User getUser() {
return users.get();
}
public void setUser(User user) {
users.set(user);
}
public void clear() {
users.remove();;
}
}
二、实现自己的拦截器
(1)判断用户是否为登录状态
public class PassportInterceptor implements HandlerInterceptor {
@Autowired
private LoginTicketDAO loginTicketDAO;
@Autowired
private UserDAO userDAO;
@Autowired
private HostHolder hostHolder;
/**
* 预处理回调方法,实现处理器的预处理
* 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或处理器
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String ticket = null;
if (httpServletRequest.getCookies() != null) {
for (Cookie cookie : httpServletRequest.getCookies()) {
//先在reponse中拿到ticket
if (cookie.getName().equals("ticket")) {
ticket = cookie.getValue();
break;
}
}
}
//把找到的ticket与数据库的做比较,如果ticket存在而且没过期,说明为登录状态
if (ticket != null) {
LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket);
if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 0) {
return true;
}
//查到当前用户ID,加入hostHolder中
User user = userDAO.selectById(loginTicket.getUserId());
hostHolder.setUser(user);
}
//true表示继续流程,继续进行其他拦截器
return true;
}
/**
* 后处理回调方法,实现处理器(controller)的后处理,但在渲染视图之前
* 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
//把用户信息设置进modelAndView
if (modelAndView != null && hostHolder.getUser() != null) {
modelAndView.addObject("user", hostHolder.getUser());
}
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,
* 如性能监控中我们可以在此记录结束时间并输出消耗时间,
* 还可以进行一些资源清理,类似于try-catch-finally中的finally,
* 但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
hostHolder.clear();
}
}
(2)判断当前用户是否有权限能访问该资源
public class LoginRequiredInterceptor implements HandlerInterceptor {
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//假设这个资源时要用户登录才能访问的,你没有登录就会跳转会原来页面:可以应用于有些视频只要VIP用户可以看
if (hostHolder.getUser() == null) {
httpServletResponse.sendRedirect("/reglogin?next=" + httpServletRequest.getRequestURI());
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
四、把拦截器交给spring管理,注意拦截器先后顺序
public class WendaWebConfiguration extends WebMvcConfigurerAdapter {
@Autowired
PassportInterceptor passportInterceptor;
@Autowired
LoginRequiredInterceptor loginRequiredInterceptor;
// 多个拦截器组成一个拦截器链
// addPathPatterns 用于添加拦截规则,/**表示拦截所有请求
// excludePathPatterns 用户排除拦截
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor);
//拦截 /user/** 的所有请求
registry.addInterceptor(loginRequiredInterceptor).addPathPatterns("/user/*");
super.addInterceptors(registry);
}
}
注意:
以下WebMvcConfigurerAdapter 比较常用的重写接口
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
/** 添加拦截器 **/
void addInterceptors(InterceptorRegistry registry);
/** 这里配置视图解析器 **/
/** 视图跳转控制器 **/
void addViewControllers(ViewControllerRegistry registry);
void configureViewResolvers(ViewResolverRegistry registry);
/** 配置内容裁决的一些选项 **/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 视图跳转控制器 **/
void addViewControllers(ViewControllerRegistry registry);
/** 静态资源处理 **/
void addResourceHandlers(ResourceHandlerRegistry registry);
/** 默认静态资源处理器 **/
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
但是在spring5.0版本后这个类WebMvcConfigurerAdapter 被丢弃了 ,虽然还可以用,但是看起来不好。在spring 5.0后要使用Java8,而在Java8中接口是可以有default方法的,所以这个类就没必要了。所以我们只需要在自定义配置类中直接实现 WebMvcConfigurer 接口就好了。
解决方法:升级到spring boot2.0后,继承WebMvcConfigurationSupport配置无效