【瑞吉外卖模块-权限认证】

下面我将深入讲解瑞吉外卖项目的权限认证模块,其核心设计围绕用户登录状态和权限的验证与控制。

🔐 权限认证核心思想

瑞吉外卖项目的权限认证主要确保用户必须登录才能访问后台系统,并对不同用户角色进行权限控制。它通过一套由过滤器、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

🔄 权限认证流程详解

这个流程主要依赖于一个核心的登录校验过滤器,它像是系统门户的“安全主管”。

  1. 拦截请求:过滤器会拦截所有到达后台的请求 (urlPatterns = "/*")。
  2. 路径检查:过滤器会检查请求的URI,对于明确的公开资源(如登录/退出接口、静态资源/backend/**等),直接放行。
  3. 会话验证:对于需要处理的请求,过滤器会检查HttpSession中是否存在登录时设置的用户标识(例如employee属性)。
  4. 处理结果
    • 已登录:过滤器放行,请求继续执行。
    • 未登录:过滤器通过输出流返回特定的错误信息(如R.error("NOTLOGIN")),前端根据此信息跳转到登录页面

⌨️ 关键代码与配置

  1. 登录状态校验与权限控制核心 - 过滤器 (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确保其被扫描并生效。

  2. 登录认证与会话管理 - 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,作为后续请求的认证凭证

  3. 退出登录 - 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 SecurityApache Shiro

瑞吉外卖的权限认证模块通过过滤器Session管理,清晰有效地实现了基于登录状态的访问控制,为理解和实现Web系统权限控制提供了良好范本。

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值