这系列的文章目的是让懂一点Java编程经验的人能够迅速的入门并且掌握shiro这类安全框架的使用。所以我们不讲原理,也不谈shiro的具体内部实现,我们的目标是在有限的时间内,迅速的掌握shiro框架并且学以致用,不会过多的谈及这么做的原因以及其背后的原理。废话不多说,我希望能直达我们的痛点,直接解决我们的目前的问题。
问题:如何利用shiro框架达到认证和授权的目的?作为一个安全的框架,认证和授权是两大组成部分,认证是为了证明我是谁,授权则是我能有什么可操作的权限。直接上代码。
目录
6. 在shiroTest.java的同名文件夹下新建shiro.ini文件用于存储用户名和密码等信息
8.1 现在很多博客或者培训机构的课程仍然在用过时的这段代码来初始化,这很误导了人,让人浪费了不少时间。
2.1 在1的基础上,我们更改shiro.ini文件,加入以下的内容来表示有一定的角色和权限
2.2 先验证用户是否成功登陆,也即验证用户是否认证成功,shiro认为授权发生在认证成功之后,否则,没有授权一说。
2.3 我们根据在shiro.ini填写的代码可以知道,用户zhangsan有admin角色,并且对应有add和update的操作权限,那么我们来写代码测试一下是否根据文件所说具有对应角色和权限
1. 如何使用shiro中的iniRealm来认证
1. eclipse创建maven项目
如图所示
maven项目命名
2. 在pom.xml文件中引入shiro相关maven库
在dependencies节点下插入以下的代码片段
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3. 在src文件夹下建立shiro包
如图所示
4. 在shiro包下新建shiroTest.java文件
5. shiroTest.java文件代码如下
public class ShiroTest {
@Test
public void Test() {
// 创建默认SecurityManager用于管理security
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
// 创建IniRealm对象用于从ini文件中读取信息
IniRealm iniRealm = new IniRealm("classpath:shiro/shiro.ini");
// 设置iniRealm到默认的SecurityManager
defaultSecurityManager.setRealm(iniRealm);
// 使用securityUtils工具类装载默认的securitymanager
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 从securityUtils获取对应的subject对象
Subject subject = SecurityUtils.getSubject();
// 设置登录的用户名和密码令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("zhangsan", "123");
// 使用subject.login模拟登陆过程
subject.login(usernamePasswordToken);
}
}
6. 在shiroTest.java的同名文件夹下新建shiro.ini文件用于存储用户名和密码等信息
[users]
zhangsan=123
wangwu=456
7. 编译运行shiroTest.java文件,效果图如下
说明了测试用例通过,shiro完成了模拟登陆的过程
8. 注意事项
8.1 现在很多博客或者培训机构的课程仍然在用过时的这段代码来初始化,这很误导了人,让人浪费了不少时间。
//加载配置文件,并获取工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取安全管理者实例
SecurityManager sm = factory.getInstance();
首先说明,自shiro1.2.x以上的版本,iniSecurityManagerFactory工厂模式的代码就已经被废弃了,至于为什么,暂时不在这篇文章讨论,若使用shiro1.2.x以上的版本,请采用文章中的IniRealm代码。
8.2 关于subject.login加异常处理块的问题
我希望我们能够反向思考一个问题
8.2.1 当我们在token中填写的用户名或者密码在ini文件中不存在时,程序会报什么异常?
8.2.2 当我们在token中填写的用户名在ini文件中存在,但对应填写的密码和ini文件密码不符合时,程序会报什么异常?
8.2.3 当我们在token中填写的密码在ini存在,但对应填写的用户名和ini不符合时,程序会报什么异常?
我希望我们能够多思考一些,通过程序报出的异常错误反过来推,错误的地方在哪里,这样有利于我们很好的快速的debug
为什么本该在subject.login代码加try-catch块我没有加上,原因就是以上。我希望大家看到这里,能够暂停一下,多多思考一下,从错误地方推异常,从异常报告推错误地方。
答案揭晓
8.2.1的用户名或者密码不存在的问题,程序报如下异常
大家可以看到程序报UnKnownAccountException异常,意思就是未知的账户异常。
8.2.2的密码不对的问题,程序报如下异常
大家可以看到程序报IncorrectCredentialsException异常,意思是错误的认证异常,表示密码不对
8.2.3的用户名不对问题,程序报如下异常
大家可以看到情况和8.2.1的一致都是报未知用户的异常
8.3 以上的异常处理仅仅是为了让大家思考在不同的错误情况下,程序会发生什么样的情况,然后根据异常报告,能够快速的debug。为了代码风格和标准,都应该在subject.login处加异常块,如下代码段所示
// 设置try-catch块捕捉验证login登录异常
try {
subject.login(usernamePasswordToken);
System.out.println("shiro验证登录成功");
} catch (UnknownAccountException uae) {
System.out.println("未知的账户名");
} catch (IncorrectCredentialsException ice) {
System.out.println("密码错误");
} catch (Exception e) {
System.out.println("shiro验证异常");
}
2. 如何使用shiro中的iniRealm来授权
2.1 在1的基础上,我们更改shiro.ini文件,加入以下的内容来表示有一定的角色和权限
//表示用户名=密码,角色
[users]
zhangsan=123,admin
wangwu=456,adminplus
//表示角色=权限
[roles]
admin=add,update
adminplus=add,update,delelte,get
然后我们更改shiroTest.java文件
2.2 先验证用户是否成功登陆,也即验证用户是否认证成功,shiro认为授权发生在认证成功之后,否则,没有授权一说。
// 验证当前用户是否登录成功
System.out.println(subject.getPrincipal() + "是否登录成功?" + subject.isAuthenticated());
// 登出当前用户
subject.logout();
// 验证当前用户是否登出
System.out.println(subject.getPrincipal() + "是否登录成功?" + subject.isAuthenticated());
代码如上,直接在1的基础上,在文件后添加以上代码就好,运行测试得到如下结果就表示用户已经通过认证了。
2.3 我们根据在shiro.ini填写的代码可以知道,用户zhangsan有admin角色,并且对应有add和update的操作权限,那么我们来写代码测试一下是否根据文件所说具有对应角色和权限
先是测试角色代码
// 使用ArrayList存储角色
List<String> roles=new ArrayList<String>();
roles.add("admin");
roles.add("adminplus");
roles.add("adminpro");
// 循环测试角色
for(String role:roles) {
System.out.println("当前用户是"+subject.getPrincipal()+"\t 是否具有"+role+"角色 \t"+subject.hasRole(role));
}
运行测试如图表示角色是否拥有
其次是测试角色所具有的权限代码
// 使用arraylist存放权限组
List<String> permissions=new ArrayList<String>();
permissions.add("update");
permissions.add("delete");
permissions.add("add");
permissions.add("get");
// 循环验证当前用户是否具有对应的权限
for(String permission:permissions)
{
System.out.println("当前用户是"+subject.getPrincipal()+"\t 是否具有"+permission+"权限 \t"+subject.isPermitted(permission));
}
运行测试如下表示权限是否拥有
3. 总结
3.1 认证的主要核心代码
创建默认的DefaultSecurityManager对象->创建iniRealm数据源对象->设置iniRealm对象到DafaultSecurityManager管理器->使用SecurityUtils加载DefaultSecurityManager管理器->从SecurityUtils获取当前的Subject对象->使用UsernamePasswordToken创建用户名和密码的token->Subject.login方法进行登录认证(记得加try-catch块处理异常)
3.2 授权的主要核心代码
Subject.getPrinciPal()获取到当前的用户名->Subject.isAuthenticated()验证是否认证通过->Subject.hasRole()验证是否具有对应的角色->Subject.isPermitted()验证是否具有对应的权限