Spring boot 搭建个人博客系统(三)——权限管理功能
一直想用Spring boot 搭建一个属于自己的博客系统,刚好前段时间学习了叶神的牛客项目课受益匪浅,乘热打铁也主要是学习,好让自己熟悉这类项目开发的基本流程。系统采用Spring boot+MyBatis+MySQL的框架进行项目开发。
项目源码:Jblog
个人主页:tuzhenyu’s page
原文地址:Spring boot 搭建个人博客系统(三)——权限管理功能
0. 思路
系统权限管理功能用来添加存储区分系统用户,进而实现系统的权限管理。博客系统权限可以简单分为博主权限和普通用户权限,普通用户权限可以浏览文章,发表评论或者赞踩等,博主权限除此之外还可以添加文章,管理系统等。未登录用户只能浏览文章不能发表评论或者赞踩等。
权限管理一般有三种常见的思路:利用Spring拦截器进行访问权限验证,利用Spring AOP进行访问权限验证和直接使用Spring Security集成模块进行访问权限验证。
- 利用Spring拦截器进行访问权限验证是通过拦截器在请求到达控制器之前拦截请求进行相应的权限验证。
- 利用Spring AOP进行访问权限验证是利用AOP切面编程的特点,可以在所有控制器函数内织入权限验证模块代码,当请求到达控制器之后会进行响应的权限验证。
- Spring Security是Spring的一个集成的安全框架,其底层原理也是集成众多的拦截器对url进行拦截从而实现响应的权限管理。
三种实现权限管理的方法相比较之下,Spring Security作为一个框架使用简单功能完善但较为笨重;Spring AOP织入权限验证代码比较方便但是不够灵活;系统选用利用Spring拦截器进行访问权限验证,根据具体需求插入相应的拦截器,使用较为方便。
1. 数据模型
权限管理需要验证用户信息,需要操纵数据库的user表和login_ticket表,这两个表的数据模型已在上一篇Spring boot 搭建个人博客系统(二)——登录注册功能中详细说明,这里不再赘述;
2. Spring拦截器
Spring拦截器主要是通过Interceptor实现类来实现,主要用来拦截用户的请求并进行相应的处理。拦截器能够在用户请求进入控制器之前将请求拦截下来并进行处理。请求先进入拦截器的preHandle()函数进行处理,处理结束后进入控制器,在控制器处理结束后,页面渲染之前会进入拦截器的postHandle()函数处理,最终在页面渲染结束后进入拦截器的afterCompletion()函数处理。
preHandle(): 这个方法在业务处理器处理请求之前被调用,SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor ,多个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
postHandle():这个方法在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。在 postHandle()函数链式调用结束后才会进行页面渲染。
afterCompletion():该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
3. 获取用户信息
结合上一篇Spring boot 搭建个人博客系统(二)——登录注册功能中的免密码登录,拦截用户访问页面的请求,获取请求中的Cookie信息进而判断用户是否有免密码登陆凭证和凭证是否过期。如果用户Cookie存在登录凭证并且凭证有效,则可以通过凭证识别用户信息并将访问设定为已登录状态。如果用户Cookie中不存在凭证或者登录凭证已经过期则将访问设定为未登录状态。已登录状态和未登录状态在具体页面访问时受访问权限管理。
@Component
public class PassportInterceptor implements HandlerInterceptor{
@Autowired
private LoginTicketDao loginTicketDao;
@Autowired
private JedisService jedisService;
@Autowired
private UserDao userDao;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String ticket = null;
if (httpServletRequest.getCookies()!=null){
for (Cookie cookie : httpServletRequest.getCookies()){
if ("ticket".equals(cookie.getName())){
ticket = cookie.getValue();
break;
}
}
}
if (ticket!=null){
LoginTicket loginTicket = loginTicketDao.seletByTicket(ticket);
if (loginTicket==null||loginTicket.getExpired().before(new Date())||loginTicket.getStatus()!=0){
return true;
}
User user = userDao.seletById(loginTicket.getUserId());
hostHolder.setUser(user);
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
if (modelAndView!=null){
modelAndView.addObject("user",hostHolder.getUser());
}
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
hostHolder.clear();
}
}
将拦截器添加到系统中:
@Component
public class WebConfiguration extends WebMvcConfigurerAdapter{
@Autowired
private PassportInterceptor passportInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor);
super.addInterceptors(registry);
}
}
4. 访问权限管理
用户在访问页面的时候可以分为已登录状态和未登录状态,同时已登录状态又分为博主权限和普通用户权限。为了区分不同状态的权限范围,通过拦截器拦截特定请求判断用户状态,如果状态不符合则拒绝访问。比如设定拦截器拦截”/create”请求,判断用户是否已经登录,如果未登录则拒绝访问;如果用户已登录则判断用户的身份是博主还是普通用户,如果是普通用户则拒绝访问。
@Component
public class LoginRequestInterceptor implements HandlerInterceptor {
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
if (hostHolder.getUser()==null||"user".equals(hostHolder.getUser().getRole())){
httpServletResponse.sendRedirect("/in");
}
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 {
}
}
将拦截器添加到系统中:
@Component
public class WebConfiguration extends WebMvcConfigurerAdapter{
@Autowired
private PassportInterceptor passportInterceptor;
@Autowired
private LoginRequestInterceptor loginRequestInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor);
registry.addInterceptor(loginRequestInterceptor).addPathPatterns("/create");
super.addInterceptors(registry);
}
}
5. 总结
利用Spring拦截器可以拦截所有请求或部分特定用户请求作特定处理,先利用拦截器判断用户登录状态,再利用拦截器判断用户身份进而实现用户权限管理。