在上一篇文章《开源框架若依中的权限控制逻辑-菜单》中,我们介绍了若依系统中的菜单管理的相关操作,最后遗留了一个小问题,为什么菜单的“路由参数”设置为非空时,菜单不会在左侧出现。今天我们就来深入的看看若依系统的菜单返回逻辑与后台数据设计。
接口捕获
我们打开若依系统,刷新页面,通过浏览器调试窗口看看调用了哪些网络请求:
在若依首页,我们F12打开浏览器调试窗口,并切换到“网络”页面,勾选过滤条件“Fetch/XHR”,然后刷新页面。捕获的请求如下图所示:
我们依次查看各个请求分别获取了什么信息。
getInfo
getInfo的全部请求信息如下:
请求网址: http://localhost/dev-api/getInfo, 请求方法: GET。
请求标头包括Authorization,Cookie等信息。
返回信息如下:
{ "msg": "操作成功", "code": 200, "permissions": [ "*:*:*" ], "roles": [ "admin" ], "user": { "searchValue": null, "createBy": "admin", "createTime": "2021-10-12 08:45:24", "updateBy": null, "updateTime": null, "remark": "管理员", "params": {}, "userId": 1, "deptId": 103, "userName": "admin", "nickName": "若依", "email": "ry@163.com", "phonenumber": "15888888888", "sex": "1", "avatar": "", "salt": null, "status": "0", "delFlag": "0", "loginIp": "127.0.0.1", "loginDate": "2022-04-03T17:06:44.000+08:00", "dept": { "searchValue": null, "createBy": null, "createTime": null, "updateBy": null, "updateTime": null, "remark": null, "params": {}, "deptId": 103, "parentId": 101, "ancestors": null, "deptName": "研发部门", "orderNum": "1", "leader": "若依", "phone": null, "email": null, "status": "0", "delFlag": null, "parentName": null, "children": [] }, "roles": [ { "searchValue": null, "createBy": null, "createTime": null, "updateBy": null, "updateTime": null, "remark": null, "params": {}, "roleId": 1, "roleName": "超级管理员", "roleKey": "admin", "roleSort": "1", "dataScope": "1", "menuCheckStrictly": false, "deptCheckStrictly": false, "status": "0", "delFlag": null, "flag": false, "menuIds": null, "deptIds": null, "admin": true } ], "roleIds": null, "postIds": null, "roleId": null, "admin": true } }
其返回信息主要包括:权限permissions, 角色roles,当前登录用户信息user,user中又包括当前用户的基础信息,部门信息,角色信息等。
我们后台来查看其接口执行逻辑。
/** * 获取用户信息 * * @return 用户信息 */ @GetMapping("getInfo") public AjaxResult getInfo() { SysUser user = SecurityUtils.getLoginUser().getUser(); // 角色集合 Set<String> roles = permissionService.getRolePermission(user); // 权限集合 Set<String> permissions = permissionService.getMenuPermission(user); AjaxResult ajax = AjaxResult.success(); ajax.put("user", user); ajax.put("roles", roles); ajax.put("permissions", permissions); return ajax; }
首先,通过Spring-Security来根据登录token信息获取用户信息,即:
SysUser user = SecurityUtils.getLoginUser().getUser();
我们依次深入方法,其调用链条:
SecurityContextHolder.getContext().getAuthentication().getLoginUser()
其设置位置位于过滤器中:
package com.ruoyi.framework.security.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.web.service.TokenService; /** * token过滤器 验证token有效性 * * @author ruoyi */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { LoginUser loginUser = tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { tokenService.verifyToken(loginUser); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } chain.doFilter(request, response); } }
在token转换为用户信息的关键代码是:
LoginUser loginUser = tokenService.getLoginUser(request);
其方法体为:
/** * 获取用户身份信息 * * @return 用户信息 */ public LoginUser getLoginUser(HttpServletRequest request) { // 获取请求携带的令牌 String token = getToken(request); if (StringUtils.isNotEmpty(token)) { try { Claims claims = parseToken(token); // 解析对应的权限以及用户信息 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); return user; } catch (Exception e) { } } return null; }
如此,便通过将token转换为了保存在redis中的用户信息。