前后端分离权限控制设计和实现思路
Posted 爪哇笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前后端分离权限控制设计和实现思路相关的知识,希望对你有一定的参考价值。
前言
react、angular、vue
等前端框架的兴起,前后端分离的架构迅速流行。但同时权限控制也带来了问题。
RBAC
模型,基于角色的访问控制(
Role-Based Access Control
)
Apache Shrio、Spring Security
,都是基于此模型设计的。权限系统可以说是整个系统中最基础,同时也可以很复杂的,在实际项目中,会遇到多个系统,多个用户类型,多个使用场景,这就需要具体问题具体分析,但最核心的
RBAC
模型是不变的,我们可以在其基础上进行扩展来满足需求。
Token
,其中会附带一些基本的用户信息,不建议附带角色权限信息。用户向后端发送请求都会附带这个
Token
。
ID
实时获取菜单信息并渲染。按钮控制的话,情况比较复杂,如果要求不是很高可以一次性查询出来,放入本地缓存,进行本地鉴权。
hasRole: function(roles){
var roleNames = this.userInfo.roleNames;
if(roleNames!= ""){
var role = roles.split( ",");
var array = roleNames.split( ",");
for( var i= 0;i<role.length;i++){
if( array.indexOf(role[i])> -1){
return true;
}
}
}
return false;
}
<i-button type= "primary" v- if= "hasRole('admin')" icon= "ios-cloud-download" @click= "exportCashReports">导出< /i-button>
后端控制
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean (SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl( "/login.html");
shiroFilterFactoryBean.setUnauthorizedUrl( "/403");
Map< String, Filter> filtersMap = new LinkedHashMap<>();
filtersMap.put( "kickout", kickoutSessionControlFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
Map< String, String> filterChainDefinitionMap = new LinkedHashMap<>();
/**
* 静态文件
*/
filterChainDefinitionMap.put( "/css/**", "anon");
filterChainDefinitionMap.put( "/images/**", "anon");
filterChainDefinitionMap.put( "/js/**", "anon");
filterChainDefinitionMap.put( "/file/**", "anon");
/**
* 登录
*/
filterChainDefinitionMap.put( "/login.html", "anon");
filterChainDefinitionMap.put( "/sys/logout", "anon");
filterChainDefinitionMap.put( "/sys/login", "anon");
/**
* 管理后台
*/
filterChainDefinitionMap.put( "/sys/**", "roles[admin]");
filterChainDefinitionMap.put( "/**", "kickout,authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 角色列表
*/
@PostMapping( "/list")
@RequiresRoles(value= "admin")
public Result list(SysRole role){
return sysRoleService .list(role);
}
token
进行校验,这里参考
Shrio
的实现思路,自己撸一个注解来实现权限校验。
/**
* 权限注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresRoles {
/**
* A single String role name or multiple comma-delimitted role names required in order for the method
* invocation to be allowed.
*/
String [] value();
/**
* The logical operation for the permission check in case multiple roles are specified. AND is the default
* @since 1.1.0
*/
Logical logical() default Logical .OR;
}
/**
* @Description
* @Author 小柒2012
* @Date 2020/6/12
*/
@Component
public class AuthUtils {
@Autowired
private RedisUtils redisUtils;
public boolean check(Object handler,String userId){
HandlerMethod handlerMethod = (HandlerMethod) handler;
Annotation permAnnotation= handlerMethod.getMethod().getAnnotation(RequiresPermissions.class);
if(permAnnotation!= null) {
String[] role = handlerMethod.getMethod().getAnnotation(RequiresPermissions.class).value();
List<String> roleList = Arrays.asList(role);
/**
* 获取用户实际权限
*/
List<Object> list = redisUtils.lGet( "perms:"+userId, 0, -1);
List<String> permissions = roleList.stream().filter(item -> list.contains(item)).collect(toList());
if (permissions.size() == 0) {
return false;
}
} else{
Annotation roleAnnotation= handlerMethod.getMethod().getAnnotation(RequiresRoles.class);
if(roleAnnotation!= null){
String[] role = handlerMethod.getMethod().getAnnotation(RequiresRoles.class).value();
List<String> roleList = Arrays.asList(role);
/**
* 获取用户实际角色
*/
List<Object> list = redisUtils.lGet( "roles:"+userId, 0, -1);
List<String> roles = roleList.stream().filter(item -> list.contains(item)).collect(toList());
if (roles.size() == 0) {
return false;
}
}
}
return true;
}
}
以上是关于前后端分离权限控制设计和实现思路的主要内容,如果未能解决你的问题,请参考以下文章