第一步:
需要注解实现权限拦截,先创建自定义注解类:
import java.lang.annotation.*;
/**
* 角色注解
*
*/
@Target({ElementType.METHOD}) // 方法注解
@Retention(RetentionPolicy.RUNTIME) // 运行时
//@Documented
public @interface Role{
// 默认
String value() default "user";
// 拥有其中一个角色就通过
String[] roles() default {};
// 必须这些角色才通过
String[] rolesMust() default {};
}第二步:
然后就自定义一个拦截器类(继承HandlerInterceptorAdapter就行了):
为了简单理解,我这里从前端发送一个 userId 来模拟登录,如果有说明登录了,没有就没登录。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
/**
* 自定义一个权限拦截器, 继承HandlerInterceptorAdapter类
*/
@Component // 交给spring管理
public class RoleInterceptor extends HandlerInterceptorAdapter {
// 在调用方法之前执行拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// System.out.println("经过了RoleInterceptor拦截器");
// 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 从方法处理器中获取出要调用的方法
Method method = handlerMethod.getMethod();
// 获取出方法上的Role注解
Role role = method.getAnnotation(Role.class);
// 如果注解为null, 说明不需要拦截, 直接放过
if (role == null) {
return true;
}
// 从前端直接发一个userId来模拟登录,如果有说明登录了,没有就没登录
String userId = (String) request.getParameter("userId");
// 没有信息说明没有登陆
if (userId == null || userId.length()==0) {
//自定义登录异常
throw new LoginException();
}
// 到数据库权限表中查询用户拥有的角色集合, 进行对比完成权限校验
List<String> userRole = 到数据库查;
if (userRole != null && userRole.size()>0) {
// 写了注解说明需要权限
// 拥有其中一个角色就通过
if (role.roles().length > 0) {
// 获取多个角色
// 如果权限配置不为空, 则取出配置值
String[] roles = role.roles();
// 校验角色权限操作,自己实现.....
// 如果角色校验不通过,在这可以不处理,留到最后处理
} else if (role.rolesMust().length > 0) {
// 必须有这些角色才通过
String[] rolesMust = role.rolesMust();
// 校验角色权限操作,自己实现.....
// 如果角色校验不通过,在这可以不处理,留到最后处理
} else {
// 默认user
String value = role.value();
for (String str : userRole) {
if (value.equals(str)) return true;
}
}
}
// 说明没登录抛出自定义的角色异常,没有自定义异常可以直接返回false,代码就不会执行下去
throw new RoleException();
// 拦截之后应该返回公共结果, 这里没做处理
// return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// System.out.println("postHandler");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// System.out.println("afterCompletion");
}
}说明: 拦截器返回的只能是boolean类型,如果想返回其他值是不行的。但是一般我们前端都要有简单清晰友好的信息
像这样的:
{
“code”:“ok”,
“data”:“成功”
}像这种json数据,拦截器就不能返回。所以我们可以自定义异常,这样在抛出异常后,再用全局异常类来处理异常的时候就可以返回我们所需要的数据。
创建自定义异常 LoginException:
package com.xoppen.user.exception;
import com.xoppen.common.entity.StatusMessage;
/**
* 登录异常
* @Author: Xu
* @Date: 2019/7/9 10:12
*/
public class LoginException extends Exception{
public LoginException(Exception e) {
super(e);
}
public LoginException(String msg, Exception e) {
super(msg, e);
}
public LoginException(String msg) {
super(msg);
}
public LoginException(){
super(StatusMessage.ERROR_LOGIN);
}
}创建全局异常处理类 BaseExceptionHandler :
package com.xoppen.user.exception;
import com.xoppen.common.entity.Result;
import com.xoppen.common.entity.ResultUtil;
import com.xoppen.common.entity.StatusCode;
import com.xoppen.common.entity.StatusMessage;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 统一异常处理类
*/
@ControllerAdvice
public class BaseExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
// ResultUtil 这是返回状态码和提示信息的工具类
//分别处理具体异常
// 业务异常直接反馈给用户
if (e instanceof LoginException) {
//登陆异常
return ResultUtil.error(StatusCode.ERROR_LOGIN, e.getMessage());
}else if (e instanceof RoleException){
// 角色异常
return ResultUtil.error(StatusCode.ERROR_ACCESS, e.getMessage());
}else if (e instanceof HttpRequestMethodNotSupportedException) {
// 请求方式异常
return ResultUtil.error(StatusCode.ERROR, StatusMessage.ERROR_REQUEST);
}else if (e instanceof DataAccessResourceFailureException){
// 数据源连接超时
return ResultUtil.error(StatusCode.ERROR, StatusMessage.ERROR_DATABASE);
} else if (e instanceof ErrorException){
return ResultUtil.error(StatusCode.ERROR, StatusMessage.ERROR);
}
// 未知异常返回统一报错信息
return ResultUtil.error( StatusCode.ERROR_SYSTEM, e.getLocalizedMessage());
}
}
这样就可以返回我们所需要的信息了;
第三步:
配置拦截器的配置类(继承WebMvcConfigurationSupport ),让拦截器生效:
import com.xoppen.user.interceptor.JwtInterceptor;
import com.xoppen.user.interceptor.RoleInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* 配置拦截器的拦截路径
*/
@Configuration //表明是个配置类
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private RoleInterceptor roleInterceptor;
protected void addInterceptors(InterceptorRegistry registry) {
//注册拦截器要声明拦截器对象和要拦截的请求
// 角色拦截器
registry.addInterceptor(roleInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login/**","/**/register/**");
//addInterceptor(roleInterceptor) 配置拦截器
//addPathPatterns("/**") 拦截器需要拦截哪些路径
//excludePathPatterns("/**/login/**","/**/register/**") 拦截器不需要拦截哪些路径
}
}第四步:
在controller上的方法直接写注解就行了:
像我自定义的角色注解 有多种写法, 写法有很多。主要的是你在校验角色的时候怎么处理(重要)
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 用户Controller
*/
@RestController //将返回的数据转成Json数据
@CrossOrigin //解决跨域问题
@RequestMapping("/user") //添加前缀
public class UserController {
// 像我自定义的角色注解有多种写法,写法有很多。主要的是你在校验角色的时候怎么处理
// 注解第一种
// 这种就一个单单注解,没有参数
@Role
@RequestMapping(value = "/name")
public Result name1(){
System.out.println("通过啦!!!!!!");
}
// 第二种
@Role(roles = {"user","admin"})
@RequestMapping(value = "/name")
public Result name2(){
System.out.println("通过啦!!!!!!");
}
// 第三种
@Role(rolesMust = {"user","admin"})
@RequestMapping(value = "/name")
public Result name3(){
System.out.println("通过啦!!!!!!");
}
// 第四种
@Role(roles = {"user","admin"},rolesMust = {"user","admin"})
@RequestMapping(value = "/name")
public Result name4(){
System.out.println("通过啦!!!!!!");
}
}这样就配置完成啦!!!!!
----------------------------------------------------------完毕---------------------------------------------------------