Spring MVC+Spring Security如何实现动态端点的动态权限访问控制
实现方案
方案1:自定义Spring Security表达式+注解鉴权(推荐)
这种方式利用Spring Security原生的扩展能力,代码侵入性低,适配注解式鉴权习惯。
- 第一步:自定义权限校验工具类
实现一个Spring管理的Bean,封装课程角色查询逻辑,示例代码:@Component("coursePermissionChecker") public class CoursePermissionChecker { @Autowired private UserCourseRoleDao userCourseRoleDao; public boolean hasCourseRole(Long courseId, String... roles) { // 获取当前登录用户ID Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null) return false; Long userId = ((LoginUser) auth.getPrincipal()).getUserId(); // 兼容全局管理员:有管理员角色直接放行 if (auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) { return true; } // 查询用户在对应课程的角色匹配 List<String> userRoles = userCourseRoleDao.listUserCourseRoles(userId, courseId); return Arrays.stream(roles).anyMatch(userRoles::contains); } } - 第二步:在Controller层直接使用注解鉴权
借助SpEL表达式直接获取路径参数,调用自定义校验逻辑,同时根据角色返回不同视图:
注意需要先开启Spring Security的方法级注解:在配置类上加@GetMapping("/course/{courseId}") @PreAuthorize("@coursePermissionChecker.hasCourseRole(#courseId, 'TEACHER', 'STUDENT')") public String getCourseDetailPage(@PathVariable Long courseId, Model model) { // 额外判断角色返回对应视图 if (coursePermissionChecker.hasCourseRole(courseId, "TEACHER")) { // 教师视图可以额外加载公告编辑、成绩管理等模块数据 model.addAttribute("isTeacher", true); return "course/teacher_detail"; } model.addAttribute("isTeacher", false); return "course/student_detail"; }@EnableGlobalMethodSecurity(prePostEnabled = true)
方案2:拦截器统一处理(适合批量接口校验)
如果大量接口都携带courseId路径参数,不想逐个加注解,可以用Spring MVC拦截器统一处理:
- 实现
HandlerInterceptor接口,在preHandle阶段从URI提取courseId,完成权限校验,把查询到的课程角色存入请求上下文:@Component public class CoursePermissionInterceptor implements HandlerInterceptor { @Autowired private CoursePermissionChecker permissionChecker; private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); // 匹配/course/xxx 路径提取courseId if (antPathMatcher.match("/course/{courseId}", uri)) { Map<String, String> pathVars = antPathMatcher.extractUriTemplateVariables("/course/{courseId}", uri); Long courseId = Long.parseLong(pathVars.get("courseId")); // 权限校验不通过返回403 if (!permissionChecker.hasCourseRole(courseId, "TEACHER", "STUDENT")) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } // 把角色存入request供后续业务使用 request.setAttribute("userCourseRole", permissionChecker.getUserCourseRole(courseId)); } return true; } } - 在Spring MVC配置类中注册拦截器,指定拦截路径即可。
视图层适配
如果不想做多视图拆分,也可以把用户的课程角色存入Model,在JSP中通过标签判断显示不同模块:
<c:if test="${isTeacher}"> <!-- 教师专属的公告发布、成员管理模块 --> <div class="teacher-operation"> <button onclick="publishNotice()">发布公告</button> </div> </c:if>
内容的提问来源于stack exchange,提问作者Delsh
相关产品推荐
相关产品推荐

