很早之前就用shiro安全框架写登录。还没用到权限,单纯的登陆。
最近的项目不得不深入研究shiro
那时候使用都是即查即用。
现在不得不深入。于是遇到了一座大山,导致了所有的问题产生
SecurityUtils.getSubject().getPrincipal()
基础配置一个简洁的shiro框架需要
- shiroRealm
- shiroConfiguration
两个java文件,再加上maven依赖就可以基本的实现所有的权限登陆操作。
至于怎么配置可以网上查,大多数一样。
先到ShiroRealm的使用方法
使用ShiroRealm extends AuthorizingRealm
然后实现其抽象类
有两个方法
- AuthenticationInfo
- AuthorizationInfo
第一个为实现登录
第二个为实现权限
AuthenticationInfo
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
此处token的为可以引用的参数
通过进入UsernamePasswordToken可以看到它的参数有
private String username;
private char[] password;
private boolean rememberMe = false;
private String host;
token.getUsername()
正常的操作是从token中getUserName。然后数据库查到userName对应的所有信息来参照密码
if (user != null) {
Object principal = user.getUserName();
Object credentials = user.getPassword();
String realmName = getName();
ByteSource credentialsSalt = ByteSource.Util.bytes(principal);
return new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
}
这里的 ByteSource.Util.bytes(principal);
是我自己实现的md5加密方法
最后返回
SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)
得到对应密码
AuthorizationInfo
它实现的只是查找权限和角色
我们先看下实现代码
User user = (User) SecurityUtils.getSubject().getPrincipal();
String userName = user.getUsername();
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 获取用户角色集
List<Role> roleList = this.roleService.findUserRole(userName);
Set<String> roleSet = roleList.stream().map(Role::getRoleName).collect(Collectors.toSet());
simpleAuthorizationInfo.setRoles(roleSet);
// 获取用户权限集
List<Menu> permissionList = this.menuService.findUserPermissions(userName);
Set<String> permissionSet = permissionList.stream().map(Menu::getPerms).collect(Collectors.toSet());
simpleAuthorizationInfo.setStringPermissions(permissionSet);
return simpleAuthorizationInfo;
上面用了jdk8的特性stream。语法糖是个好东西
通过实例化SimpleAuthorizationInfo
分别存入
- setRoles
- setStringPermissions
最后 return simpleAuthorizationInfo;
获得对应的权限和角色
这里可以看到我遇到的山了
SecurityUtils.getSubject().getPrincipal()
这里有一个问题
AuthorizationInfo
它到底什么时候会被引用到?
按照我的理解,它也只有需要操作到权限时候才会调用这个函数。
有几种方式
- 通过config的配置
我用的是SpringBoot,不需要xml配置,直接新建一个java声明。其中config中需要实现不少配置,如下的过滤器
@Bean(name = “shiroFilter”)
public ShiroFilterFactoryBean shiroFilterFactoryBean(
org.apache.shiro.mgt.SecurityManager securityManager)
我后面会提到 - 通过前端页面html的javascript调用
- @RequiresPermissions(value ="")
在controller中可以在@RequestMapping下判定权限
我们看一下这一句
User user = (User) SecurityUtils.getSubject().getPrincipal();
这里的getPrincipal();
它得到的是一个Object类型。从源码来看,它的实现是用迭代器遍历Object的每一个属性的。
重点就是这里,他是哪里来的?
那时候调用了这个函数,它给我报了个错
java.lang.String cannot be cast to com.b2b.java.po.UserPo
通过logger日志输出,得到getPrincipal的值是String。单纯的String类型。
通过我的查找。真正声明principal的函数是
AuthenticationInfo。的return 语句
return new SimpleAuthenticationInfo(user, credentials, credentialsSalt, getName());
//这里是我修改好了的
因为没有认真研究源码的参数所以误传值进去
Object principal = user.getUserName();
真正的应该是
Object principal = user;
我们看看它的源码
* @param principal the 'primary' principal associated with the specified realm.
* @param hashedCredentials the hashed credentials that verify the given principal.
* @param credentialsSalt the salt used when hashing the given hashedCredentials
* @param realmName the realm from where the principal and credentials were acquired.
public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
this.principals = new SimplePrincipalCollection(principal, realmName);
this.credentials = hashedCredentials;
this.credentialsSalt = credentialsSalt;
}
上面的注释已经很清楚了
所以应该传入Object类型。
小结
以后报错的时候必须看源码