一、前言
本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限
基本环境
spring-boot 2.1.8
mybatis-plus 2.2.0
mysql 数据库
maven项目
Spring Security入门学习可参考之前文章:
二、数据库建表
表关系简介:
用户表t_sys_user 关联 角色表t_sys_role 两者建立中间关系表t_sys_user_role
角色表t_sys_role 关联 权限表t_sys_permission 两者建立中间关系表t_sys_role_permission
最终体现效果为当前登录用户所具备的角色关联能访问的所有url,只要给角色分配相应的url权限即可
温馨小提示:这里逻辑根据个人业务来定义,小编这里讲解案例只给用户对应的角色分配访问权限,像其它的 直接给用户分配权限等等可以自己实现
表模拟数据如下:
三、Spring Security 动态权限控制
1、未登录访问权限控制
自定义AdminAuthenticationEntryPoint类实现AuthenticationEntryPoint类
这里是认证权限入口 -> 即在未登录的情况下访问所有接口都会拦截到此(除了放行忽略接口)
温馨小提示:ResponseUtils和ApiResult是小编这里模拟前后端分离情况下返回json格式数据所使用工具类,具体实现可参考文末给出的demo源码
@Component
public class AdminAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
ResponseUtils.out(response, ApiResult.fail("未登录!!!"));
}
}
2、自定义过滤器MyAuthenticationFilter继承OncePerRequestFilter实现访问鉴权
每次访问接口都会经过此,我们可以在这里记录请求参数、响应内容,或者处理前后端分离情况下,以token换用户权限信息,token是否过期,请求头类型是否正确,防止非法请求等等
logRequestBody()方法:记录请求消息体
logResponseBody()方法:记录响应消息体
【注:请求的HttpServletRequest流只能读一次,下一次就不能读取了,因此这里要使用自定义的MultiReadHttpServletRequest工具解决流只能读一次的问题,响应同理,具体可参考文末demo源码实现】
@Slf4j
@Component
public class MyAuthenticationFilter extends OncePerRequestFilter {
private final UserDetailsServiceImpl userDetailsService;
protected MyAuthenticationFilter(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("请求头类型: " + request.getContentType());
if ((request.getContentType() == null && request.getContentLength() > 0) || (request.getContentType() != null && !request.getContentType().contains(Constants.REQUEST_HEADERS_CONTENT_TYPE))) {
filterChain.doFilter(request, response);
return;
}
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(response);
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
// 记录请求的消息体
logRequestBody(wrappedRequest);
// String token = "123";
// 前后端分离情况下,前端登录后将token储存在cookie中,每次访问接口时通过token去拿用户权限
String token = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
log.debug("后台检查令牌:{}", token);
if (StringUtils.isNotBlank(token)) {
// 检查token
SecurityUser securityUser = userDetailsService.getUserByToken(token);
if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
throw new AccessDeniedException("TOKEN已过期,请重新登录!");
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
// 全局注入角色权限信息和登录用户基本信息
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(wrappedRequest, wrappedResponse);
} finally {
stopWatch.stop();
long usedTimes = stopWatch.getTotalTimeMillis();
// 记录响应的消息体
logResponseBody(wrappedRequest, wrappedResponse, usedTimes);
}
}
private String logRequestBody(MultiReadHttpServletRequest request) {
MultiReadHttpServletRequest wrapper = request;
if (wrapper != null) {