下面我将深入讲解瑞吉外卖项目的权限认证模块,其核心设计围绕用户登录状态和权限的验证与控制。
🔐 权限认证核心思想
瑞吉外卖项目的权限认证主要确保用户必须登录才能访问后台系统,并对不同用户角色进行权限控制。它通过一套由过滤器、Session机制和权限控制逻辑组成的流程来实现。
为了让你对这套流程有个宏观的认识,我梳理了它的核心工作过程,如下图所示:
flowchart TD
A[用户请求] --> B{请求是否需要登录?}
B -- 否 --> C[直接放行]
B -- 是 --> D{Session中是否存在<br>用户标识?}
D -- 存在 --> E[执行请求]
D -- 不存在 --> F[返回“NOTLOGIN”<br>并跳转登录页]
subgraph F [前端处理未登录响应]
F1[前端接收到“NOTLOGIN”]
F2[跳转到登录页面]
end
C --> E
🔄 权限认证流程详解
这个流程主要依赖于一个核心的登录校验过滤器,它像是系统门户的“安全主管”。
- 拦截请求:过滤器会拦截所有到达后台的请求 (
urlPatterns = "/*")。 - 路径检查:过滤器会检查请求的URI,对于明确的公开资源(如登录/退出接口、静态资源
/backend/**等),直接放行。 - 会话验证:对于需要处理的请求,过滤器会检查HttpSession中是否存在登录时设置的用户标识(例如
employee属性)。 - 处理结果:
- 已登录:过滤器放行,请求继续执行。
- 未登录:过滤器通过输出流返回特定的错误信息(如
R.error("NOTLOGIN")),前端根据此信息跳转到登录页面。
⌨️ 关键代码与配置
-
登录状态校验与权限控制核心 - 过滤器 (LoginCheckFilter):
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*") @Slf4j public class LoginCheckFilter implements Filter { // 路径匹配器,支持通配符 public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 1.获取本次请求的URI String requestURI = request.getRequestURI(); log.info("拦截到请求:{}", requestURI); // 定义不需要处理的请求路径 (白名单) String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**", "/front/**" }; // 2.判断本次请求是否需要处理 boolean check = check(urls, requestURI); // 3.如果不需要处理,则直接放行 if (check) { log.info("本次请求{}不需要处理", requestURI); filterChain.doFilter(request, response); return; } // 4.判断登录状态,如果已登录,则直接放行 if (request.getSession().getAttribute("employee") != null) { log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("employee")); filterChain.doFilter(request, response); return; } log.info("用户未登录"); // 5.如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据 response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN"))); } /** * 路径匹配,检查本次请求是否需要放行 */ public boolean check(String[] urls, String requestURI) { for (String url : urls) { boolean match = PATH_MATCHER.match(url, requestURI); if (match) { return true; } } return false; } }此过滤器使用
AntPathMatcher进行通配符匹配,@WebFilter注解将其标记为过滤器,@ServletComponentScan确保其被扫描并生效。 -
登录认证与会话管理 - Controller中的登录逻辑:
@PostMapping("/login") public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) { // 1. MD5加密密码 String password = employee.getPassword(); password = DigestUtils.md5DigestAsHex(password.getBytes()); // 2. 查询数据库 LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Employee::getUsername, employee.getUsername()); Employee emp = employeeService.getOne(queryWrapper); // 3. 用户不存在、密码错误、账号禁用等校验 if (emp == null) { return R.error("登录失败"); } if (!emp.getPassword().equals(password)) { return R.error("登录失败"); } if (emp.getStatus() == 0) { return R.error("账号已禁用"); } // 4. 登录成功,将员工id存入Session request.getSession().setAttribute("employee", emp.getId()); return R.success(emp); }登录成功后,将员工ID存入Session,作为后续请求的认证凭证。
-
退出登录 - Controller中的退出逻辑:
@PostMapping("/logout") public R<String> logout(HttpServletRequest request) { // 清理Session中保存的当前登录员工的id request.getSession().removeAttribute("employee"); return R.success("退出成功"); }退出时移除Session中的用户标识。
💡 设计要点与注意事项
- 会话存储:登录成功后,将用户ID存入Session,这是后续权限验证的依据。
- 路径匹配:过滤器使用
AntPathMatcher进行灵活的白名单路径匹配。 - 响应处理:过滤器直接通过
response.getWriter().write返回JSON信息,前端据此进行页面跳转。 - Long型ID精度问题:后端返回前端Long类型ID(如雪花算法ID)可能因JS精度丢失。常用解决方案是配置消息转换器,将Long类型转为String返回。
- 扩展角色权限控制:基础版本主要依赖Session进行登录状态验证。如需更细粒度的角色权限控制(RBAC),可参考以下思路:
- 在用户表或新增角色表中定义用户角色。
- 使用拦截器或AOP,在过滤登录状态后,进一步校验用户访问特定接口的权限。
- 结合注解(如
@RequiresRoles("admin"))进行声明式权限控制。
🚀 扩展思路
- 数据权限:可结合
ThreadLocal存储当前用户ID,在查询数据时自动过滤(如WHERE create_user = ?)。 - JWT替代Session:考虑无状态和分布式,可用JWT替代Session管理登录状态。
- 整合安全框架:对于复杂权限系统,可考虑整合Spring Security或Apache Shiro。
瑞吉外卖的权限认证模块通过过滤器和Session管理,清晰有效地实现了基于登录状态的访问控制,为理解和实现Web系统权限控制提供了良好范本。
676





