一、背景介绍
最近项目中要实现根据不同用户去划分不同的角色,而不同角色具备调用不同接口的权限这个功能。用户在调用接口时需要校验用户是否具有权限访问接口,防止外界恶意调用随意篡改
二、思路&方案
为什么要进行接口鉴权?
- 接口鉴权可以提供对接口访问的监控和追踪功能。通过记录和审计用户对接口的访问情况,可以及时发现异常行为和安全威胁,并采取相应的措施进行应对。
- 通过接口鉴权,可以限制对敏感数据的访问。只有具有相应权限的用户或应用程序才能获取敏感数据,从而保护数据的机密性和完整性。
- 接口鉴权可以实现对接口的细粒度访问控制。根据用户的身份、角色、权限等级等,可以控制用户对接口的不同操作和功能的访问权限,确保只有有权用户可以执行相应的操作。
接口鉴权是保证系统安全、数据保护和访问控制的重要机制,防止未经授权的访问、滥用和数据泄露,提高系统的安全性和可靠性。
@PerAuthorizre权限注解
作用:方法前拦截判断是否具备权限,用来控制被注解标记的类或方法是否能够被调用
好处:
安全性增强:只有经过授权的用户才能执行带有 @PerAuthorizr 注解的代码,从而减少了潜在的安全漏洞和攻击风险。
标记权限:通过使用 @PerAuthorizr 注解,可以明确地标记哪些代码需要特定的权限才能执行。这有助于开发人员更好地了解代码的权限需求,并确保只有具有相应权限的用户或角色可以访问该代码。
底层实现:
- 底层实现可能会使用 AOP 技术。通过 AOP,可以在代码执行之前或之后,根据注解的参数进行权限验证和授权操作。
- 可能会使用拦截器或过滤器来拦截请求,并进行权限验证和授权操作。拦截器或过滤器可以在请求到达目标代码之前对请求进行预处理,包括检查用户的权限和身份验证。
- 可能会使用缓存机制。通过缓存用户的权限信息,可以避免频繁地进行权限查询和验证,提高系统的响应速度。
使用:
@PreAuthorize("")调用别名为ss类的hasPermi方法,并传入参数
@ss注解
作用:自定义,定义了权限校验流程
结果:返回true则鉴权成功,false则返回403说明没有权限
使用:
@ss.hasPermi('system:role:list')
调用别名为ss类的hasPermi方法,并传入参数
逻辑校验流程
第一步、定义@ss类,并添加hasPermi校验方法
- 获取传入用户的信息
- 判断用户信息的权限信息集合中是否含有定义的权限
第二步、在要鉴权的接口上添加@PreAuthorizez注解
三、过程
NS图
引入依赖
<!-- spring security 安全认证 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId><version>5.5.8</version>
</dependency>
定义@ss类,并添加hasPermi校验方法
package com.example;import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Set;/*** @BelongsPackage: com.example* @Description: 自定义权限实现* @Version: 1.0*/
@Service("ss")
public class PermissionService {/** 所有权限标识 */private static final String ALL_PERMISSION = "*:*:*";/*** 验证用户是否具备某权限** @param permission 权限字符串* @return 用户是否具备某权限*/public boolean hasPermi(String permission) throws Exception {//判断是否传入权限字符if ( StringUtils.isEmpty(permission)){return false;}//获取用户信息LoginUser loginUser = SecurityUtils.getLoginUser();//判断是否有用户信息,或者用户信息中是否包含权限集合if ( StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())){return false;}//将权限设置到请求头中PermissionContextHolder.setContext(permission);//判断是否包含权限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));}
}
字符串工具类
package com.example;/*** @BelongsPackage: com.example* @Description: 字符串工具类* @Version: 1.0*/
public class StringUtils {/** 空字符串 */private static final String NULLSTR = "";/*** * 判断一个对象是否为空* @return true:为空 false:非空*/public static boolean isNull(Object object){return object == null;}/*** * 判断一个字符串是否为空串* @return true:为空 false:非空*/public static boolean isEmpty(String str){return isNull(str) || NULLSTR.equals(str.trim());}/*** 去空格*/public static String trim(String str){return (str == null ? "" : str.trim());}
}
权限信息
package com.example;import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;/*** @BelongsPackage: com.example* @Description: TODO* @Version: 1.0*/
public class PermissionContextHolder {private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT";public static void setContext(String permission){RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission,RequestAttributes.SCOPE_REQUEST);}public static String getContext(){return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES,RequestAttributes.SCOPE_REQUEST));}
}
安全服务工具类
package com.example;import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;/*** @BelongsPackage: com.example* @Description: 安全服务工具类* @Version: 1.0*/
public class SecurityUtils {/*** 获取用户**/public static LoginUser getLoginUser() throws Exception {try{return (LoginUser) getAuthentication().getPrincipal();}catch (Exception e){throw new Exception("获取用户信息异常");}}/*** 获取Authentication*/public static Authentication getAuthentication(){return SecurityContextHolder.getContext().getAuthentication();}
}
登录用户身份权限
package com.example;import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;/*** @BelongsPackage: com.example* @Description: 登录用户身份权限* @Version: 1.0*/
@Data
public class LoginUser implements UserDetails
{private static final long serialVersionUID = 1L;/*** 用户ID*/private Long userId;/*** 部门ID*/private Long deptId;/*** 用户唯一标识*/private String token;/*** 登录时间*/private Long loginTime;/*** 过期时间*/private Long expireTime;/*** 登录IP地址*/private String ipaddr;/*** 登录地点*/private String loginLocation;/*** 浏览器类型*/private String browser;/*** 操作系统*/private String os;/*** 权限列表*/private Set<String> permissions;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return null;}@Overridepublic String getUsername() {return null;}@Overridepublic boolean isAccountNonExpired() {return false;}@Overridepublic boolean isAccountNonLocked() {return false;}@Overridepublic boolean isCredentialsNonExpired() {return false;}@Overridepublic boolean isEnabled() {return false;}
}
四、总结
本文章对@PerAuthorizre注解结合项目具体如何进行鉴权进行了详细分析,我们在项目使用的过程中除了知道怎么用,也要知其所以然。
如果大家想要了解 spring sercurity+token安全机制是如何做的权限认证和授权,关于SpringSecurity如何集成JWT做的认证授权大家可以参考这篇博主的文章:SpringSecurity集成JWT认证框架_jwt spring security_哆木的博客-CSDN博客)