最近学习了 权限框架shiro的知识,做一下 学习的笔记
使用ini
这是shiro 最简单的用法,首先创建一个demo.ini
文件,里面写入如下的内容
[users]
xiezihao=123456,admin
[roles]
admin = user.insert,user.update
下面对上面的配置进行一下解释
[users] 代表的是用户,之后在代码中做登录的时候使用,比如这里的配置中,的一是就是 ,用户名是xiezihao,密码是123456,拥有一个admin的角色。
[roles] 代表的是角色的意思,一个用户可以对应有多个角色,这里的一是是,admin这个角色拥有,user.insert 和 user.update 两种操作的权限
下面将通过示例代码演示,如何利用这个ini文件实现登录,和权限的认证
public void t(){
//设置使用的realm
IniRealm iniRealm = new IniRealm("classpath:demo.ini");
//新建一个安全管理器,并将realm设置到这个管理器中去
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
//将设置好的 安全管理器放入安全工具类中,然后用 安全工具类来获取subject
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//创建登录用的token
UsernamePasswordToken token = new UsernamePasswordToken();
token.setUsername("xiezihao");
token.setPassword("123456".toCharArray());
//登录
subject.login(token);
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
if(subject.hasRole("admin")){
System.out.println("拥有admin的身份");
}
if(subject.isPermitted("user.insert")){
System.out.println("拥有 user.insert 的权限");
}
}
后面的例子其实都和这个比较的相似,都是按照如下顺序
- 拿到一个realm
- 用这个realm 设置出一个安全管理器
- 为安全工具类设置安全管理器,然后以此创建出subject
- 利用subject做后续的操作
自定义的realm
有的时候框架给出的realm不能满足我们实际的需求,我们可以自定义realm,新建一个类,让这个类去继承 AuthorizingRealm
然后实现里面的两个方法:doGetAuthorizationInfo
授权、doGetAuthenticationInfo
登录认证
下面用一个实际的例子 来说明
public class DemoRealm extends AuthorizingRealm {
//授权的时候执行
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("开始鉴权操作");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
HashSet<String> roles = new HashSet<>();
HashSet<String> permissions = new HashSet<>();
//将用户的角色和权限填入set集合 在实际的情况下这一步会从数据库中获取
roles.add("admin");
permissions.add("user.insert");
//将用户的角色集合和权限集合放入到AuthorizationInfo中去
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
//登录时候执行
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("开始用户登录操作");
//获取用户登录的用户名和密码
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
char[] password = usernamePasswordToken.getPassword();
//这里我们模拟一下权限认证,实际情况上这里会从数据库拿数据
if (username.equals("xiezihao") && new String(password).equals("123456")) {
/**
* 如果认证成功返回一个AuthenticationInfo对象,这三个参数分别是
* 用户名、密码、和realm名字 realm的名字随便取好了主要是用来区分的作用
*/
return new SimpleAuthenticationInfo("xiezihao", "123456", "demoRealm");
}
//如果登录失败就返回null
return null;
}
}
下面我们利用一个测试类来测试一下。测试类的写法可以参考 ini 这个测试的方法
@Test
public void demoRealmT() {
// 新建一个 我们自定义的realm
DemoRealm demoRealm = new DemoRealm();
//建立安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(demoRealm);
//构建 subject
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken();
usernamePasswordToken.setUsername("xiezihao");
usernamePasswordToken.setPassword("123456".toCharArray());
//登录
subject.login(usernamePasswordToken);
if(subject.isAuthenticated()){
System.out.println("用户已经成功登录");
}
if(subject.hasRole("admin")){
System.out.println("用户拥有admin角色");
}
if(subject.isPermitted("user.insert")){
System.out.println("用户拥有user.insert 操作的权限");
}
}
最后输出的结果如下
开始用户登录操作
用户已经成功登录
开始鉴权操作
用户拥有admin角色
开始鉴权操作
用户拥有user.insert 操作的权限
jdbc realm
前面的操作中我们使用的都是写死或者本地文件中写入用户配置,但是在实际的过程中我们肯定是从数据库中读取的
shiro提供了一个JdbcRealm
的类用来查询数据库,但是这个类里的sql 语句默认都是写死的,我们可以进入到这个类的源码里去看下
/**
* The default query used to retrieve account data for the user.
*/
protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
/**
* The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN.
*/
protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
/**
* The default query used to retrieve the roles that apply to a user.
*/
protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
/**
* The default query used to retrieve permissions that apply to a particular role.
*/
protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
在这个类的开头定义了 这4个sql 语句,从上到下的用处分别是:
- 用户账号密码登录
- 用户带salt的账号密码登录
- 查询用户名下所有的角色
- 查询角色下所有的权限
所以我们只需要把数据库的表按照这个sql 语句的形式来创建就可以了,但是这样子做肯定是不行的,因为实际的业务字段名可能不是这样的,所有我们可以重新自定义sql 语句,按照刚才源码里的格式 实际的操作请看下面的代码
@Test
public void test(){
//设置一个数据库连接池,这里使用了阿里巴巴的druid连接池
DruidDataSource source = new DruidDataSource();
source.setUrl("jdbc:mysql://localhost:3306/rili");
source.setUsername("root");
source.setPassword("root");
//创建一个jdbcrealm 并加你个连接池设置到这个realm中去
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(source);
//自定义登录sql
String sql = "select password from user where user_name = ?";
jdbcRealm.setAuthenticationQuery(sql);
//自定义角色sql
String sql2 = "select quan from role where name = ?";
jdbcRealm.setUserRolesQuery(sql2);
//自定义权限sql
String sql3= "select permission from permissions where juese = ?";
jdbcRealm.setPermissionsQuery(sql3);
/**
* 这里是很重要的一句!必须要设置他为true,源代码里的解释是默认人false
* 表示只有用户名和角色关联,如果要角色和权限关联必须设置为true
*/
jdbcRealm.setPermissionsLookupEnabled(true);
//之后的操作步骤就是和之前的一样就不再做复述了
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(jdbcRealm);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xiezihao","12345");
subject.login(token);
if(subject.isAuthenticated()){
System.out.println("login ok");
}
subject.checkRoles("admin");
subject.checkPermissions("user.update");
spring 整合shiro
到上面为止,介绍了shiro的基本使用方法,下面讲解的是如何将shiro和spring进行整合。
引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
创建一个基本的springboot工程,引入必要的web依赖,这里关于springboot就不再做描述了
编写自定义realm
public class MyRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//获取登录的用户名
String username = (String) principalCollection.getPrimaryPrincipal();
//授权操作,模拟从数据库获取授权信息
if (username.equals("admin")) {
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addStringPermission("user:insert");
} else {
simpleAuthorizationInfo.addRole("default");
}
return simpleAuthorizationInfo;
}
//登入认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取登录的用户名和密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
//认证,这里应该从数据库获取,为了简便就直接写了
if (username.equals("admin") && password.equals("12345")) {
return new SimpleAuthenticationInfo("admin", "12345", "MyRealm");
} else {
return null;
}
}
}
编写shiro 配置类
这里是重要的一步,定义一个类进行授权的配置