新闻详情

新闻详情

首页 / 资讯中心 / 详情

若依-代码学习01

发布时间:2026/9/28 4:37:17来源:尧图网络
若依-代码学习01
登录问题解释一下这三行代码AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message(user.login.success))); LoginUser loginUser (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUserId());为什么要记录两遍日志以及authentication.getPrincipal()这个代码的作用是解释三者区别一句话行写到哪目的第 1 行sys_logininfor异步登录审计流水第 2 行内存对象拿到当前登录用户第 3 行sys_user更新该用户最近登录 IP/时间之后才会执行tokenService.createToken(loginUser)生成 JWT 返回给前端。token是uuid问题我发现若依的token是随机uuid/** * 创建令牌 * * param loginUser 用户信息 * return 令牌 */ public String createToken(LoginUser loginUser) { String token IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); MapString, Object claims new HashMap(); claims.put(Constants.LOGIN_USER_KEY, token); claims.put(Constants.JWT_USERNAME, loginUser.getUsername()); return createToken(claims); }解释不完全是“随便发个 UUID 当令牌”。我先把createToken/ JWT / Redis 这几段串起来看。对了一半IdUtils.fastUUID()生成的是 Redis 会话键不是最终返回给前端的那个 token。两层“token”名字实际是什么作用String token IdUtils.fastUUID()随机 UUID作为 Redis key 的一部分关联整份LoginUserreturn createToken(claims)JWT 字符串发给前端Header 里带的那个流程是1. 生成 uuid2. Redis: login_tokens:{uuid} LoginUser权限、用户信息、过期时间等3. 把 uuid 放进 JWT 的 claimslogin_user_key4. 用 secret 做 HS512 签名得到真正的 JWT 返回前端对应代码public String createToken(LoginUser loginUser) { String token IdUtils.fastUUID(); // ① Redis 会话标识 loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); // ② 写入 Redis MapString, Object claims new HashMap(); claims.put(Constants.LOGIN_USER_KEY, token); // ③ uuid 塞进 JWT claims.put(Constants.JWT_USERNAME, loginUser.getUsername()); return createToken(claims); // ④ 返回签名后的 JWT }请求进来时反过来解析 JWT → 取出里面的 uuid → 用 uuid 去 Redis 取 LoginUser为什么这样设计JWT 本身很“瘦”不塞完整用户/权限改权限可直接改 Redis不用等 JWT 过期。UUID 只是会话 ID随意生成没问题安全靠JWT 有secret签名伪造不了Redis 里有会话数据删了就登出强制下线也靠这个。所以UUID 是会话钥匙前端拿到的是包着这把钥匙的 JWT。若依token校验拦截器问题那当前项目是不是 都会有一个拦截器如果没有token校验通过就会直接阻拦解释是的思路对但精确说不是 MVC 拦截器而是 Spring Security JWT 过滤器。两层配合1.JwtAuthenticationTokenFilter过滤器每个请求都会进这个 Filter从 Header 取 JWT → 解析出 uuid → 去 Redis 取LoginUser→ 放进SecurityContext。注意它本身不会直接 return 拦截没 token / token 无效时只是不往 SecurityContext 里塞用户然后继续往下走。2.SecurityConfig真正决定拦不拦requests.requestMatchers(/login, /register, /captchaImage).permitAll() .requestMatchers(HttpMethod.GET, /, /*.html, /**.html, /**.css, /**.js, /profile/**).permitAll() .requestMatchers(/swagger-ui.html, /v3/api-docs/**, /swagger-ui/**, /druid/**).permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated();白名单登录、注册、验证码、静态资源等permitAll不需要登录。其余全部authenticated()SecurityContext 里没有认证用户就拦截。拦截后由AuthenticationEntryPointImpl返回 401提示认证失败。请求流程简化请求进来→ JwtFilter有有效 token有则写入 SecurityContext→ Security 规则白名单 → 放行其他 → 已认证放行 : 401 拦住补充业务接口上还有PreAuthorize等做权限校验能登录 ≠ 有某个菜单/按钮权限。那是第二道门第一道是上面的登录态认证。A01-菜单表的perms字段问题我不太懂sys_menu表中的perms这个字段是干啥的。都长这个样子【system:role:list】解释perms是权限标识permission string用来做按钮/接口级鉴权不是用来画菜单的。它存什么一段字符串约定俗成写成模块:功能:操作例如system:user:list— 用户列表system:user:add— 用户新增system:user:edit— 用户修改目录M一般为空菜单C常带*:list按钮F才是各类操作权限的主力。它怎么用登录后系统会按「用户 → 角色 → 菜单」查出该用户拥有的所有perms放进权限集合。之后两处比对这个字符串后端接口PreAuthorize(ss.hasPermi(system:user:add))没有对应perms就调不通接口。前端按钮v-hasPermi[system:user:add]没有对应perms就隐藏「新增」等按钮。两边写的字符串必须和sys_menu.perms里配置的一致。和菜单的关系容易混概念作用path/component能不能看到、打开哪个页面perms能不能点某个按钮、调某个接口所以能进「用户管理」页不等于能新增用户新增还得角色勾了带system:user:add的那条按钮菜单。一句话perms 权限开关的名字角色勾了哪些菜单用户就拿到哪些名字前后端拿这些名字做鉴权。A02-那为什么目录是空的问题如果sys_menu表中的menu_type如果是M即代表目录的时候perms这个字段是空的解释因为目录本身不对应任何可执行操作只负责把菜单组织成树。在若依里目录M只是分组如「系统管理」没有页面、没有接口所以不需要perms留空即可。菜单C对应一个页面常配xxx:list用来控制「能不能进这个功能/查列表」。按钮F对应具体操作增删改导出等perms才是真正被前后端拿去鉴权的。权限实际是挂在菜单和按钮上的。角色勾选时勾了某个页面下的按钮就拿到那些perms父级目录空着不影响——它既不参与PreAuthorize也不参与v-hasPermi。简单说目录只负责“归类显示”不负责“能不能操作”所以perms为空是正常设计。以下是 目录和菜单的一一对应树形结构代码-解析代码01-selectMenuTreeByUserIdOverride public ListSysMenu selectMenuTreeByUserId(Long userId) { ListSysMenu menus null; if (SecurityUtils.isAdmin(userId)) { menus menuMapper.selectMenuTreeAll(); } else { menus menuMapper.selectMenuTreeByUserId(userId); } return getChildPerms(menus, MENU_ROOT_ID); }02-getChildPerms/** * 根据父节点的ID获取所有子节点 * * param list 分类表 * param parentId 传入的父节点ID * return String */ public ListSysMenu getChildPerms(ListSysMenu list, long parentId) { ListSysMenu returnList new ArrayListSysMenu(); for (IteratorSysMenu iterator list.iterator(); iterator.hasNext();) { SysMenu t (SysMenu) iterator.next(); // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 if (t.getParentId() parentId) { recursionFn(list, t); returnList.add(t); } } return returnList; }03-recursionFn/** * 递归列表 * 作用是得到该父节点的直系子节点列表 * param list 分类表 * param t 子节点 */ private void recursionFn(ListSysMenu list, SysMenu t) { // 得到该父节点的直系子节点列表 ListSysMenu childList getChildList(list, t); t.setChildren(childList); // 判断孩子有没有孩子 for (SysMenu tChild : childList) { if (hasChild(list, tChild)) { recursionFn(list, tChild); } } }问题你能否举个具体的实际例子然后我看一下排序的整个过程解释好用一组具体菜单数据把整条链路走一遍。这里的「排序过程」指的是递归建树时一步步怎么挂节点getChildList本身不按orderNum排序顺序取决于查库时列表的先后。1. 初始扁平列表list假设从数据库查出来是这样已按order_num排好序号menuIdmenuNameparentId01系统管理01100用户管理12101角色管理131001用户查询10041002用户新增10052系统监控06200在线用户2调用getChildPerms(list, 0)—— 找根节点parentId 0。2.getChildPerms先找顶级节点遍历listparentId 0的有两个系统管理(1)→ 调用recursionFn(list, 系统管理)再加入结果系统监控(2)→ 调用recursionFn(list, 系统监控)再加入结果3. 对「系统管理」做recursionFn逐步展开第 1 层t 系统管理(1)getChildList → parentId 1 的节点→ [用户管理(100), 角色管理(101)]setChildren → 系统管理.children [用户管理, 角色管理]接着遍历这两个子节点第 2 层-AtChild 用户管理(100)hasChild→ 有1001、1002→ 再递归getChildList → parentId 100→ [用户查询(1001), 用户新增(1002)]setChildren → 用户管理.children [用户查询, 用户新增]再看叶子用户查询hasChild false → 停用户新增hasChild false → 停第 2 层-BtChild 角色管理(101)hasChild→ 没有子节点 → 不递归children保持空/未再挂此时「系统管理」这棵子树已经成型系统管理(1)├── 用户管理(100)│ ├── 用户查询(1001)│ └── 用户新增(1002)└── 角色管理(101)4. 对「系统监控」做recursionFngetChildList → parentId 2→ [在线用户(200)]setChildren → 系统监控.children [在线用户]在线用户hasChild false → 停系统监控(2)└── 在线用户(200)5. 最终返回结果[系统管理(1)├── 用户管理(100)│ ├── 用户查询(1001)│ └── 用户新增(1002)└── 角色管理(101),系统监控(2)└── 在线用户(200)]6. 调用顺序小结像栈一样getChildPerms(parentId0)│├─ recursionFn(系统管理)│ ├─ setChildren([用户管理, 角色管理])│ ├─ recursionFn(用户管理)│ │ ├─ setChildren([用户查询, 用户新增])│ │ ├─ 用户查询 无子 → 停│ │ └─ 用户新增 无子 → 停│ └─ 角色管理 无子 → 停│└─ recursionFn(系统监控)├─ setChildren([在线用户])└─ 在线用户 无子 → 停核心就三步循环找儿子 → 挂到 children → 儿子还有孙子就继续递归。如果你想看的是orderNum怎么影响最终菜单显示顺序可以说一下我再按 SQL/order_num那条线补一版。全局安全规则SecurityConfig代码/** * spring security配置 * * author ruoyi */ EnableMethodSecurity(prePostEnabled true, securedEnabled true) Configuration public class SecurityConfig { /** * 认证失败处理类 */ Autowired private AuthenticationEntryPointImpl unauthorizedHandler; /** * 退出处理类 */ Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token认证过滤器 */ Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; /** * 跨域过滤器 */ Autowired private CorsFilter corsFilter; /** * 允许匿名访问的地址 */ Autowired private PermitAllUrlProperties permitAllUrl; /** * 身份验证实现 */ Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } /** * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 * denyAll | 用户不能访问 * fullyAuthenticated | 用户完全认证可以访问非remember-me下自动登录 * hasAnyAuthority | 如果有参数参数表示权限则其中任何一个权限可以访问 * hasAnyRole | 如果有参数参数表示角色则其中任何一个角色可以访问 * hasAuthority | 如果有参数参数表示权限则其权限可以访问 * hasIpAddress | 如果有参数参数表示IP地址如果用户IP和参数匹配则可以访问 * hasRole | 如果有参数参数表示角色则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ Bean protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity // CSRF禁用因为不使用session .csrf(csrf - csrf.disable()) // 禁用HTTP响应标头 .headers((headersCustomizer) - { headersCustomizer.cacheControl(cache - cache.disable()).frameOptions(options - options.sameOrigin()); }) // 认证失败处理类 .exceptionHandling(exception - exception.authenticationEntryPoint(unauthorizedHandler)) // 基于token所以不需要session .sessionManagement(session - session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 注解标记允许匿名访问的url .authorizeHttpRequests((requests) - { permitAllUrl.getUrls().forEach(url - requests.requestMatchers(url).permitAll()); // 对于登录login 注册register 验证码captchaImage 允许匿名访问 requests.requestMatchers(/login, /register, /captchaImage).permitAll() // 静态资源可匿名访问 .requestMatchers(HttpMethod.GET, /, /*.html, /**.html, /**.css, /**.js, /profile/**).permitAll() .requestMatchers(/swagger-ui.html, /v3/api-docs/**, /swagger-ui/**, /druid/**).permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated(); }) // 添加Logout filter .logout(logout - logout.logoutUrl(/logout).logoutSuccessHandler(logoutSuccessHandler)) // 添加JWT filter .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) // 添加CORS filter .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) .addFilterBefore(corsFilter, LogoutFilter.class) .build(); } /** * 强散列哈希加密实现 */ Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } }问题讲些一下这个SecurityConfig类需要学习的东西呗。跟接口没关系的吧解释对它本身不是业务接口类不是 Controller而是 Spring Security 的全局安全规则配置。但它会拦所有 HTTP 请求含/login、业务 API所以学若依权限体系时绕不开。这类要学什么1. 它在整条链路里的位置请求进来后大致是CORS→JWT Filter→Security 鉴权规则→ ControllerSecurityConfig负责定规则 挂过滤器不写具体业务逻辑。2. 四个核心 Bean / 组件组件作用SecurityFilterChain主配置CSRF、Session、谁能访问、挂哪些 FilterAuthenticationManager登录时做用户名密码认证给登录服务用BCryptPasswordEncoder密码加密/校验注入的 Handler/Filter失败处理、退出、JWT 解析、匿名 URL3. 为什么这样配若依常用套路关 CSRF前后端分离 JWT不用 Cookie SessionCSRF 意义不大STATELESS不建 Session认证信息靠 TokenpermitAllvsauthenticated白名单匿名其余必须登录EnableMethodSecurityController 上可用PreAuthorize做按钮/接口级权限4. 过滤器顺序很重要CorsFilter → JwtAuthenticationTokenFilter → UsernamePasswordAuthenticationFilter先跨域再解析 Token 塞进 SecurityContext后面才能判断“已登录”。5. 和接口的真正关系写业务接口去*Controller决定“这个 URL 要不要登录 / 匿名”看这里的authorizeHttpRequests以及Anonymous扫出来的permitAllUrl决定“登录后有没有某个权限”方法上的PreAuthorize(hasPermi(xxx))由EnableMethodSecurity打开所以不是接口代码但是接口的门禁配置。建议学习顺序本类filterChain每一段在干什么JwtAuthenticationTokenFilterToken 怎么解析、怎么放进上下文SysLoginServiceAuthenticationManager登录怎么拿到 TokenPreAuthorize/ 权限注解接口级授权如果你愿意我可以按这个顺序把JwtAuthenticationTokenFilter也拆开讲一遍。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SpringBoot+HTML校园快递代取系统:从设计到部署完整实战 2026/9/28 5:45:06

SpringBoot+HTML校园快递代取系统:从设计到部署完整实战

说实话,每次看到有人把校园快递代取系统做成“十分钟demo”——一个表单一个列表就算完事,我都想劝一句:别再浪费这个题目了。这个题目在毕业设计和简历项目里都非常经典,原因很简单:业务场景真实、角色清晰、状态流转…

阅读更多 →
UNet眼底血管分割实战:数据集切片、训练与结果文件复现 2026/9/28 5:45:05

UNet眼底血管分割实战:数据集切片、训练与结果文件复现

简介:本资源面向医学图像分割初学者与深度学习实践者,提供一套基于U-Net的眼底血管二分类分割完整方案,解决从数据准备到模型推理的全流程问题。压缩包共216个文件,以182张png切片图像、8个py脚本、1个pth权重文件及若干txt日志为…

阅读更多 →
别再用丑模板了,这份microsoft免费网站保姆级建站教程救急 2026/9/28 5:45:05

别再用丑模板了,这份microsoft免费网站保姆级建站教程救急

别再用丑模板了,这份microsoft免费网站保姆级建站教程救急 别再盯着那些千篇一律、配色辣眼的模板网站发呆了。对于项目经理来说,拿着一个连基本响应式都没做好的模板去见客户,简直是职业自杀。你心里清楚, 模板网站太丑不够用…

阅读更多 →
开关电源纹波噪声抑制:从机理到工程实践 2026/9/28 5:44:58

开关电源纹波噪声抑制:从机理到工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
基于PyQt5打造YOLOv8~v13通用检测界面:TaoToken统一API接入与配置骨架 2026/9/28 5:44:57

基于PyQt5打造YOLOv8~v13通用检测界面:TaoToken统一API接入与配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
基于深度学习的老旧照片修复与修补:从退化建模到人脸先验的完整实战 2026/9/28 5:44:56

基于深度学习的老旧照片修复与修补:从退化建模到人脸先验的完整实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉