初出茅庐.开胃菜·代码思路·接口资源鉴权
初出茅庐.开胃菜·代码思路·接口资源鉴权
在看接口资源鉴权这部分代码的时候需要带着一些问题来看代码
- 1.什么是接口资源?
- 2.什么是接口资源鉴权
- 3.如何配置接口资源
- 4.系统中的接口资源是和什么绑定的(用户,角色,部门?)
- 5.CRM系统架构中是如何实现接口资源鉴权的
思路 🍐
- 先了解什么是接口资源(百度)
- 了解什么是接口资源鉴权(百度)
- 了解接口资源是和什么绑定的(查看产品原型或问产品经理(老师)),了解需求的过程可以询问产品经理
- 如果自己要实现接口资源鉴权如何实现
- CRM系统中的接口资源鉴权是如何实现的
什么是接口资源?API接口
用户哪些接口是能看到的,有哪些接口是能访问的,那么这些用户能访问的接口就称之为这个用户的接口资源,如果该用户没有对应的接口,后端应该返回403(没有权限)
简而言之:就是用户能看的、能操作的称之为 用户的接口资源,如果用户不能访问这个资源,应该返回403
思路:🍐
如果我要实现,对应的我应该知道,这些数据是还是存储在中的,如何进行配置,如何修改,然后前后端是如何交互的
1.页面配置
1. 系统中添加资源
系统管理--权限管理--菜单管理

可以配置对应的目录,目录下对应的接口
如果后续添加了新的功能,可以在这里配置目录和接口

2. 角色配置资源
在系统管理--权限管理--角色管理目录下可以点击对应角色的修改

点击修改可以修改该角色拥有的接口资源

⚠️注意:这里的权限标识符需要和代码中的权限标识符进行对应

可以添加对应的资源目录
2.关联数据库表
目录表:sys_menu

角色表:sys_role

角色和目录关联表:sys_role_menu

3.具体实现:
前端获取接口资源
首先在登录的时候回应该返回给前端这个用户具有哪些接口资源
这里很多同学会进行猜测,登录的时候,返回的,我们是做技术的,不要盲目猜测,通过前端,通过浏览器的F12看一下就知道这部分接口资源是如何获取的了
我们知道我们在登录的时候主要返回的是token,而token中并没有保存这部分接口资源信息
那么这部分的信息应该什么时候获取呢?
我们查看登录页面登录一次

可以看到在调用登录接口后又调用了一个getInfo接口,我们查看一下这个接口

我们在后端查看一下这个接口/getInfo
SysLoginController
/**
* 获取用户信息
*
* @return 用户信息
*/
@GetMapping("getInfo")
public AjaxResult getInfo() {
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
SysUser user = loginUser.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;
}
首先getInfo接口是在登录接口之后调用的
所以通过request可以获取对应的用户
通过service查询对应的角色和权限集合将其保存在结果集中返回给前端,这样前端就拥有了对应的角色和接口资源,前端会根据对应的权限标识符来判断用户能看到哪些按钮,哪些目录,具体前端怎么判断的作为后端工程师的你不用关心。
后端接口资源鉴权
利用的是Security里的一个注解@PreAuthorize
这个注解是加载Controller层的需要接口权限的接口上的,对于公共的接口人人都可以访问的接口就不需要加这个注解了
/**
* 查询线索管理列表
*/
@PreAuthorize("@ss.hasPermi('clues:clue:list')")
@GetMapping("/list")
public TableDataInfo list(TbClue tbClue) {
...
}
在controller层的接口上加上了@PreAuthorize("@ss.hasPermi('clues:clue:list')")这个注解
这个注解里的内容是ss.hasPermi('xxx:xxx:xx')
具体的实现要看PermissionService
PermissionService
package com.huike.framework.web.service;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.huike.common.core.domain.entity.SysRole;
import com.huike.common.core.domain.model.LoginUser;
import com.huike.common.utils.ServletUtils;
import com.huike.common.utils.StringUtils;
/**
* RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母
*
*
*/
@Service("ss")
public class PermissionService {
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ",";
@Autowired
private TokenService tokenService;
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
return false;
}
return hasPermissions(loginUser.getPermissions(), permission);
}
/**
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
*
* @param permission 权限字符串
* @return 用户是否不具备某权限
*/
public boolean lacksPermi(String permission)
{
return hasPermi(permission) != true;
}
/**
* 验证用户是否具有以下任意一个权限
*
* @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
* @return 用户是否具有以下任意一个权限
*/
public boolean hasAnyPermi(String permissions)
{
if (StringUtils.isEmpty(permissions))
{
return false;
}
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
{
return false;
}
Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(PERMISSION_DELIMETER))
{
if (permission != null && hasPermissions(authorities, permission))
{
return true;
}
}
return false;
}
/**
* 判断用户是否拥有某个角色
*
* @param role 角色字符串
* @return 用户是否具备某角色
*/
public boolean hasRole(String role)
{
if (StringUtils.isEmpty(role))
{
return false;
}
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
{
return false;
}
for (SysRole sysRole : loginUser.getUser().getRoles())
{
String roleKey = sysRole.getRoleKey();
if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
{
return true;
}
}
return false;
}
/**
* 验证用户是否不具备某角色,与 isRole逻辑相反。
*
* @param role 角色名称
* @return 用户是否不具备某角色
*/
public boolean lacksRole(String role)
{
return hasRole(role) != true;
}
/**
* 验证用户是否具有以下任意一个角色
*
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
* @return 用户是否具有以下任意一个角色
*/
public boolean hasAnyRoles(String roles)
{
if (StringUtils.isEmpty(roles))
{
return false;
}
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
{
return false;
}
for (String role : roles.split(ROLE_DELIMETER))
{
if (hasRole(role))
{
return true;
}
}
return false;
}
/**
* 判断是否包含权限
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
}
可以看到的是我们定义了一个Serivice并且声明了一个在IOC容器中的别名叫ss
在@PreAuthorize("@ss.hasPermi('clues:clue:list')")这个注解中的@ss其实就是从ioc容器中找到叫ss的类
调用里面的hasPermi方法
这个方法代码如下
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
return false;
}
return hasPermissions(loginUser.getPermissions(), permission);
}
/**
* 判断是否包含权限
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
其中获取用户信息的部分:
TokenService
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey);
return user;
}
return null;
}
/**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request)
{
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
{
token = token.replace(Constants.TOKEN_PREFIX, "");
}
return token;
}
根据用户里的权限列表集合和注解里的权限标识符进行比对
hasPermissions(loginUser.getPermissions(), permission);
/**
* 判断是否包含权限
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
如果包含了权限标识符则表示用户具有权限