回顾之前做了几个关于权限访问的功能,大家都会想到使用shiro进行处理,但是原生的提供的需要做适当的扩展才能满足自定义的权限控制,这时候,你就需要继承shiro的AuthorizingRealm类来实现
具体代码
@Component
public class HynRealmConfig extends AuthorizingRealm {
@Autowired
private RedisData redisData;
@Autowired //用户权限
private PermissionData permissionData;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof OAuth2Token;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
OAuth2Token loginToken=(OAuth2Token) token;
if("app".equals(loginToken.getLoginChannel())
||"oa".equals(loginToken.getLoginChannel())
||"sso".equals(loginToken.getLoginChannel())
) {
return loginByApp(token);
}else {
return loginDefault(token);
}
}
private AuthenticationInfo loginDefault(AuthenticationToken token) throws AuthenticationException {
OAuth2Token loginToken=(OAuth2Token) token;
String accToken = loginToken.getPrincipal();
//token失效,或首次登陆
if (loginUser == null) {
//首次登陆,账号密码登录
if(StringUtils.isNotBlank(((OAuth2Token) token).getUserName())) {
loginUser = userService.login(((OAuth2Token) token).getUserName(),((OAuth2Token) token).getPassword());
if (!ObjectUtils.isEmpty(loginUser)&& !org.springframework.util.StringUtils.isEmpty(loginUser.getRemark())) {
throw new UnknownAccountException(loginUser.getRemark());
}
if (loginUser == null) {
throw new UnknownAccountException("账户不存在,请确认输入!");
}
if (loginUser.getDisabled()) {
throw new UnknownAccountException("账户被禁用!");
}
List<SysRoleDTO> roleList = userService.getRoleListByuserId(loginUser.getId());
loginUser.setRoleList(roleList);
Set<String> roleSet=new TreeSet<>();
if(!CollectionUtils.isEmpty(roleList)) {
for (SysRoleDTO role : roleList) {
roleSet.add(role.getId());
}
//查询权限permission
Set<String> permissionSet= this.getPermissionList(roleSet);
if(!CollectionUtils.isEmpty(permissionSet)) {
}
}
this.createToken(loginUser,accToken);
}else {//token失效
throw new NotAuthorizedException("token失效,请重新登录");
}
}
return new SimpleAuthenticationInfo(loginUser.getId(),accToken,loginUser.getName());
}
扩展了shiro的框架,就更好跟前端交互用户的权限以及那个返回的字段需要对其接口进行隐藏处理。那么下面就是后台利用自定义接口,以及自定义AOP进行对接口进行定义以及拦截。
自定义接口比较简单大致如下
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ShiroAuth {
String[] roles() default {};
String permissions() default "";
}
可以设置用户的角色也可以设置权限列表,在接口上注释即可使用,那么使用的话,如何针对每个访问都可以实现拦截访问,答案就在于AOP进行处理,也是本文重点介绍的AOP编程
具体代码如下
@Component
@Aspect
@Order(110)
public class PermissionOperationAop {
public final static int minLen=2;
@Pointcut("@annotation(com.dream.annotation.ShiroAuth)")
public void permissionCut(){
}
@Around("permissionCut()")
public Object permissionOperation(ProceedingJoinPoint joinPoint) throws Throwable {
Object proceed=null;
MethodSignature sign = (MethodSignature)joinPoint.getSignature();
Method method = sign.getMethod();
ShiroAuth shiroAuth = method.getAnnotation(ShiroAuth.class);
String permissions = shiroAuth.permissions();
if(StringUtils.isBlank(permissions)){
proceed = joinPoint.proceed();
return proceed;
}
//判断是否有可操作的权限
String[] nodes = permissions.split(":");
String participant=null;
String others=null;
if(nodes.length<minLen){
throw new IllegalArgumentException("@ShiroAuth中的权限标识格式不正确");
}else if(nodes.length==minLen){
participant=nodes[nodes.length-1];
}else{
participant=nodes[nodes.length-2];
others=nodes[nodes.length-1];
}
Subject subject = SecurityUtils.getSubject();
boolean checkDataScope = shiroAuth.checkDataScope();
boolean permitted=false;
if(checkDataScope){
permitted = subject.isPermitted(new WildcardPermission(permissions));
}
String uid=(String)subject.getPrincipal();
//获取被植入对象的参数列表
Object[] args = joinPoint.getArgs();
for(Object arg:args){
if(arg instanceof BaseQuery){
BaseQuery baseQuery=(BaseQuery) arg;
baseQuery.setUid(uid);
if(permitted){
baseQuery.setOperatorScope(BaseQuery.global);
}
}else if(arg instanceof BaseUpdate){
BaseUpdate baseUpdate=(BaseUpdate) arg;
baseUpdate.setUid(uid);
}
}
proceed = joinPoint.proceed(args);
return proceed;
}
针对上面的代码核心部分进行重点的介绍
拦截的作用方法基于自定义接口 可以这样写
@Pointcut("@annotation(com.dream.annotation.ShiroAuth)")
public void permissionCut(){
}
以及拦截的方法作用的位置
@Around("permissionCut()")
public Object permissionOperation(ProceedingJoinPoint joinPoint) throws Throwable
然后下面的代码块就是针对自定义好的字符串,也就是用户的权限进行处理判断,相信都可以很清晰明白如何使用。