在线用户双重判断
一、用户是否在线校验(双重保障)
先经过注解进入全局拦截器;再通过异常处理器来处理
方式一:通过面向切面的思想,使用注解接口来拦截,需要传递request对象,才可以获取用户信息(全局拦截器处理)
方式二:通过全局对象(不需要传参),获取当前request对象,request对象再通过用户数据来判断用户是否在线(全局异常处理)
1.1 控制层(获取角色和权限数据)
/**
* 获取用户的角色和权限数据
* @author HuangJingNa
* @date 2019年12月22日 下午4:01:55
*
* @return
* @throws Exception
*/
@PostMapping("find_role_permission")
@CheckRoles({"role:student"})
@CheckPerssions({"permission:study", "permission:sleep"})
public Object findRolePerssion() throws Exception {
return userService.findRolePerssion();
}
1.2 进入全局拦截器(注解)
package cn.kooun.core.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import cn.kooun.service.UserService;
/**
* 全局拦截器
* @author HuangJingNa
* @date 2019年12月22日 下午4:40:45
*
*/
public class GlobalInterceptor implements HandlerInterceptor{
@Autowired
private UserService userService;
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if(handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
return baseHandler(request, response, handlerMethod);
}
return true;
}
private boolean baseHandler(
HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) {
boolean flag = true;
flag = userService.checkOnline(request, response, handlerMethod);
if(!flag) {
return flag;
}
flag = userService.checkRoles(request, response, handlerMethod);
if(!flag) {
return flag;
}
flag = userService.checkPerssions(request, response, handlerMethod);
if(!flag) {
return flag;
}
return flag;
}
}
不管是否有该注解,都会进入全局拦截器中
1.3 service层CheckOnline()中需要将登录时封装的用户对象设置在request域中
/**
* 校验是否在线
* @author HuangJingNa
* @date 2019年12月22日 下午4:48:13
*
* @param request
* @param response
* @param handlerMethod
* @return
*/
public boolean checkOnline(
HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) {
//获取方法上的注解,判断是否要进行校验
NotCheckOnline notCheckOnline = handlerMethod.getMethodAnnotation(NotCheckOnline.class);
if(notCheckOnline != null) {
//放行,不进行校验
return true;
}
//获取在线用户
Object user = RequestUtils.getUserLogin(request, response, redisService);
if(user == null) {
ResponseUtils.returnJson(
response,
ResultUtils.error("登录失效,请重新登录~", Result.JUMP_LOGIN));
return false;
}
//设置request.setAttribute(),方便全局校验获取用户数据
request.setAttribute(Dict.USER, user);
return true;
}
请求类RequestUtils
package cn.kooun.common.request;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.StringUtils;
import cn.kooun.common.redis.RedisService;
import cn.kooun.pojo.info.Dict;
/**
* 请求工具类
* @author HuangJingNa
* @date 2019年12月22日 下午4:52:10
*
*/
public class RequestUtils {
/**
* 校验用户是否在线(根据用户凭证)
* @author HuangJingNa
* @date 2019年12月22日 下午5:03:40
*
* @param request
* @param response
* @param redisService
* @return
*/
public static Object getUserLogin(
HttpServletRequest request,
HttpServletResponse response,
RedisService redisService) {
//登录校验
String userToken = request.getHeader(Dict.USER_TOKEN);
if(StringUtils.isEmpty(userToken)) {
return null;
}
//返回在线数据
return redisService.get(userToken);
}
}
相应类ReponseUtils
package cn.kooun.common.reponse;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import cn.kooun.pojo.info.Dict;
/**
* 响应工具类
* @author HuangJingNa
* @date 2019年12月22日 下午4:52:24
*
*/
public class ResponseUtils {
/**
* response响应json数据格式
*
* @author HuangJingNa
* @date 2019年12月22日 下午4:52:24
* @param response
* @param result
*/
public static void returnJson(HttpServletResponse response, Object result) {
//创建json转换的类
PrintWriter writer = null;
try {
// 避免乱码
response.setCharacterEncoding(Dict.UTF8);
// 设置ContentType
response.setContentType(Dict.JSON_HEAD);
writer = response.getWriter();
writer.append(JSON.toJSONString(result));
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(writer!=null) {
writer.close();
}
}
}
}
使用全局对象获取用户对象时才有值
/**
* 获取用户的角色和权限数据
* @author HuangJingNa
* @date 2019年12月22日 下午4:03:19
*
* @return
* @throws Exception
*/
public Object findRolePerssion() throws Exception {
//使用全局对象来判断用户是否在线
LoginUser user = (LoginUser) SecurityUtils.getCurrentUser();
return packageRolesAndPermissions(user);
}
/**
* 打包角色和权限数据
* @author HuangJingNa
* @date 2019年12月22日 下午4:30:49
*
* @param user
* @return
*/
private Object packageRolesAndPermissions(LoginUser user) {
//在线:获取权限和角色
List<String> roles = user.getRoles();
List<String> permissions = user.getPermissions();
Map<String, Object> result = new HashMap<String, Object>();
result.put("roles", roles);
result.put("permissions", permissions);
return ResultUtils.success(result);
}
1.4 权限工具SecurityUtils
package cn.kooun.common;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cn.kooun.pojo.exception.OffLineException;
import cn.kooun.pojo.info.Dict;
/**
* 权限工具
* @author HuangJingNa
* @date 2019年12月23日 下午2:44:54
*
*/
public class SecurityUtils {
/**
* 获取当前用户
* 可以作为全局对象来获取当前request对象,
* 再使用request对象(通过用户key值)来获取用户数据,来判断用户是否登录
* 方式一:通过注解接口的方式来判断用户是否在线(根据用户凭证);全局拦截器处理
* 方式二:使用全局对象来获取request对象;全局异常处理
* 以上两个方式,双重保障
* @author HuangJingNa
* @date 2019年12月23日 下午2:46:39
*
* @return
* @throws ffLineException
*/
public static Object getCurrentUser() throws Exception {
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
.getRequest();
Object user = request.getAttribute(Dict.USER);
if(user == null) {
throw new OffLineException("用户不在线~");
}
return user;
}
}
1.5 用户不在线异常处理OffLineException
package cn.kooun.pojo.exception;
/**
* 自定义异常,用户不在线抛出的异常
* @author HuangJingNa
* @date 2019年12月23日 下午2:55:38
*
*/
public class OffLineException extends Exception{
private static final long serialVersionUID = 7291020619358516087L;
public OffLineException() {
super();
}
public OffLineException(String message) {
super(message);
}
}
1.6 全局拦截器处理该自定义异常
package cn.kooun.core.exception;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import cn.kooun.common.result.Result;
import cn.kooun.common.result.ResultUtils;
import cn.kooun.pojo.exception.OffLineException;
/**
* 全局异常处理
* @author HuangJingNa
* @date 2019年12月21日 下午3:46:19
*
*/
@ControllerAdvice//标记此类为全局异常拦截器
public class GlobalExceptionHandler {
/**
* 系统异常处理,如404、500
* @author HuangJingNa
* @date 2019年12月21日 下午3:48:45
*
* @return
* @throws Exception
*/
@ExceptionHandler(value = Exception.class)//监听对应的异常对象
@ResponseBody
public Object defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception{
//控制台输出错误信息
e.printStackTrace();
if(e instanceof OffLineException) {
return ResultUtils.error("登录失效,请重新登录~", Result.JUMP_LOGIN);
}
return ResultUtils.error("系统繁忙,请联系管理员~");
}
}