token前置校验
若依后台管理token前置校验使用一个全局过滤器实现,具体类在com.ruoyi.framework.security.filter下
具体代码
@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);
}
}
业务流程
1 获取用户登入对象
2 如果登入对象存在且spring security域对象内为空说明请求是首次被过滤器拦截且用户是登入状态。
3 判断成功,刷新token,将loginUser 对象封装成authentication对象并且存入spring security域对象之中,可以让后面的控制器都能访问,然后放行。
4 放行
具体实现
代码中可以看到,通过调用tokenService实现类的getLoginUser方法可以实现登入对象的获取。点开方法
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;
}
此方法接收HttpServletRequest对象为参数,从中取出token,token为空返回null,不为空则解析token拿取负载获取key值,去redis查找之前登入存入的loginUser对象然后返回。
登入后获取用户信息
请求地址
http://localhost/dev-api/getInfo
返回数据
主要是用户权限集,角色集,用户信息
控制器在 com.ruoyi.web.controller.system下;
主要代码
@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;
}
业务流程
1 获取当前用户对象
2 获取用户角色集合(sys_role表下的role_key字段)
3 获取用户的权限集合(sys_menu表下的perms)
4 返回数据
具体实现
1 SecurityUtils.getLoginUser().getUser() 此函数用于获取spring security 域对象内的loginUser对象,如果用户是登入状态,则可以获取该对象,反之则为null
2 若依关于用户权限的五张表 sys_user(用户表),sys_user_role(用户与角色关联表),sys_role(角色表),sys_role_menu(角色与菜单关联表),sys_menu(菜单表)
通过链接sys_user,sys_user_role,sys_role三张表可以查出每个用户对应的角色,对应sql语句为
select sr.role_key from (sys_user su left join sys_user_role sur on su.user_id=sur.user_id) left join sys_role sr on sur.role_id=sr.role_id where su.user_id=?
参数为我们传递进去的用户id
通过链接sys_role,sys_role_menu,sys_menu三张表可以查出每个角色对应的权限
select sm.perms from (sys_role sr left join sys_role_menu srm on sr.role_id=srm.role_id) left join sys_menu sm on srm.menu_id=sm.menu_id where sr.role_id=? 参数为角色id
通过链接sys_user,sys_user_role,sys_role_menu,sys_menu,可以查出每个用户对应的权限
select sm.perms from (sys_user su left join sys_user_role sur on su.user_id=sur.user_id left join sys_role_menu srm on sur.role_id=srm.role_id) left join sys_menu sm on srm.menu_id=sm.menu_id where su.user_id=? 参数为用户id
如果查询的用户是管理员角色,这里返回为null,因为在sys_role_menu中没有说明管理员角色对应的权限id, 那是如何实现管理员拥有所有权限的功能呢,点开getMenuPermission函数,
public Set<String> getMenuPermission(SysUser user)
{
Set<String> perms = new HashSet<String>();
// 管理员拥有所有权限
if (user.isAdmin())
{
perms.add("*:*:*");
}
else
{
List<SysRole> roles = user.getRoles();
if (!roles.isEmpty() && roles.size() > 1)
{
// 多角色设置permissions属性,以便数据权限匹配权限
for (SysRole role : roles)
{
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
perms.addAll(rolePerms);
}
}
else
{
perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));
}
}
return perms;
}
可以看到在判断到该用户是管理员后手动给该用户添加所有权限,不会通过数据库查询权限。
用户和角色是一对多关系,角色和权限也是一对多关系,所以一个用户有多个权限多个角色