引用自百度百科,如下:
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
其实就是一个权限管理的框架。。。。还支持其它的一些特色功能;
参考了 博文shiro详解-shiro史上最全学习笔记,如下:
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro
可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。其基本功能点如下图所示:
各个功能具体的解释如下:
Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储; Web Support:Web 支持,可以非常容易的集成到 Web 环境; Caching:缓存,比如用户登录后,其用户信息、拥有的角色权限不必每次去查,这样可以提高效率;
Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
这个是外部访问shiro的流程
以下解释来自上面那篇博客,如下:
Subject:
主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject认为是一个门SecurityManager 才是实际的执行者;
SecurityManager:
安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;
Realm:
域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
也就是说对于我们而言,最简单的一个 Shiro 应用:
应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager; 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。
how2j学习教程
创建一个项目,这个不赘述;
不用添加maven项目,添加如下两个依赖包,如下:
然后创建一个User类,如下:
package com.test;
public class User {
private String name;
private String password;
// 创建一个带两个参数的构造函数,为了后面方便
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
再在这个User类的包下,创建一个shiro.ini的配置文件,如下:
其中shiro.ini配置文件信息,如下:
#定义用户
[users]
#用户名 zhang3 密码是 12345, 角色是 admin
zhang3 = 12345, admin
#用户名 li4 密码是 abcde, 角色是 产品经理
li4 = abcde,productManager
#定义角色
[roles]
#管理员什么都能做
admin = *
#产品经理只能做产品管理
productManager=addProduct,deleteProduct,editProduct,updateProduct,listProduct
#订单经理只能做订单管理
orderManager=addOrder,deleteOrder,editOrder,updateOrder,listOrder
然后创建一个实现用配置文件作为安全数据源的类,创建一个测试类TestShiro,如下:
package com.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import java.util.ArrayList;
import java.util.List;
/**
* 这是一个简易的交互模式:
* 就是在实体类中定义好用户名和密码,角色,权限
* 然后再在shiro中去认证是否能够匹配登录,和角色权限
*/
public class TestShiro {
public static void main(String[] args) {
/*
* 设置实体类中的数据
* */
User zhang3=new User("zhang3","12345");
User li4=new User("li4","abcde");
// 注意这个用户在shiro的配置文件中并不存在
User wang5=new User("wang5","999");
// 将这些用户数据添加到一个List集合中,便于后面判断
List<User> users=new ArrayList<>();
users.add(zhang3);
users.add(li4);
users.add(wang5);
// 创建实体类中的角色
List<String> roles=new ArrayList<>();
roles.add("admin");
roles.add("productManager");
// 创建实体类中角色的权限
List<String> permits=new ArrayList<>();
permits.add("addProduct");
permits.add("addOrder");
// 登录每一个用户,判断该用户是否已经在shiro中
for (User user : users) {
if(login(user)){
System.out.printf("%s \t登录成功,用的密码是%s\t",
user.getName(),user.getPassword());
System.out.println();
}else {
System.out.printf("%s \t登录失败,用的密码是%s\t",
user.getName(),user.getPassword());
System.out.println();
}
}
System.out.println("------------------------------------------------------");
// 判断登录成功的是否具有某种角色
for (User user : users) {
// 首先得获取到登录的
for ( String role : roles) {
if (login(user)){
// 再判断角色
if(isRole(role)){
System.out.printf("%s\t 拥有角色: %s\t%n",
user.getName(),role);
}else {
System.out.printf("%s\t 不拥有角色: %s\t%n",user.getName(),role);
}
}
}
}
System.out.println("------------------------------------------------------");
// 判断登录成功的是否具有某种权限
for (User user : users) {
// 首先得获取到登录的
for ( String permit : permits) {
if (login(user)){
// 再判断权限
if(isPermitted(permit)){
System.out.printf("%s\t 拥有权限: %s\t%n",
user.getName(),permit);
}else {
System.out.printf("%s\t 不拥有权限: %s\t%n",user.getName(),permit);
}
}
}
}
}
/*
* 判断当前用户是否具有某种角色
* */
private static boolean isRole(String role){
return getSubject().hasRole(role);
}
/*
* 判断当前用户是否具有某种功能
* */
private static boolean isPermitted(String permit){
return getSubject().isPermitted(permit);
}
/*
* 获取到当前用户,注意这个对象所属的包是
* org.apache.shiro.subject.Subject;
* */
private static Subject getSubject(){
// 创建默认的安全管理实例
DefaultSecurityManager manager=new DefaultSecurityManager();
// 设置安全管理的配置文件,使用iniRealm去管理这个配置文件
IniRealm iniRealm=new IniRealm("classpath:shiro.ini");
// 设置该认证,也可以理解为域,安全数据源
manager.setRealm(iniRealm);
// 将安全管理放入全局对象
SecurityUtils.setSecurityManager(manager);
// 全局对象通过安全管理者生成Subject对象
Subject subject = SecurityUtils.getSubject();
return subject;
}
/*
* 获取到当前用户,
* 需要登录才知道是当前用户是谁
* 返回值是:判断是否登录成功
* */
private static boolean login(User user){
Subject subject = getSubject();
// 如果已经登录过了,就退出
if(subject.isAuthenticated()){
subject.logout();
}
// 创建一个用户,设置用户名和密码
UsernamePasswordToken upt=new UsernamePasswordToken(user.getName(),user.getPassword());
// 登录,最终是跟Realm中的配置文件对比,是否正确
try {
subject.login(upt);
} catch (UnknownAccountException uae){
System.out.println("用户名不存在");
} catch (IncorrectCredentialsException ice){
System.out.println("密码错误");
} catch (LockedAccountException lae){
System.out.println("用户被锁定,不能登录");
} catch (AuthenticationException e){
System.out.println("认证错误,登录失败");
e.printStackTrace();
return false;
}
return subject.isAuthenticated();
}
}
上面的Realm数据源,取自在shiro.ini配置文件中来的;接下来的示例是将数据库中的存储数据源作为数据源;
既然是在数据库中取数据源,那就得在数据库中建库建表,并且其数据库的内容是跟原来在数据库中配置的一样的;
创建User表并且插入数据,表数据如下:
数据是跟原来在配置文件中写的一样的;值得注意的是,鉴于真实的项目考虑,其id的数据类型可以选择bigint,这样的数据范围更加大;
创建的角色表role,如下:
创建的权限表permission,如下:
为了让用户拥有的角色以及对应的权限,创建关系表,都是多对多关系,无论是用户还是角色,权限,都可以是多对多关系;
创建用户角色表user_role,如下:
创建角色权限表role_permission,如下:
插入的关系,就是跟原配置文件中的关系是一样的,如下:
#定义用户
#[users]
#zhang3 = 12345, admin
#li4 = abcde,productManager
#[roles]
#admin = *
#productManager=addProduct,deleteProduct,editProduct,updateProduct,listProduct
#orderManager=addOrder,deleteOrder,editOrder,updateOrder,listOrder
然后在前面的User的基础上添加一个id字段,为了对应数据库中的id字段,如下:
ps:不加id也没事,没用到,我试过了,只不过加上更加好;
package com.test;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public User() {
}
// 创建一个带两个参数的构造函数,为了后面方便
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
然后再创建一个连接数据库的DAO类,并且该类只做对数据库中的数据的查询,如下:
package com.test.dao;
import java.sql.*;
import java.util.HashSet;
import java.util.Set;
public class DAO {
// 在构造函数里面加载驱动,每次调用对象都会调用到
public DAO(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 连接数据库
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=utf-8",
"root","admin");
}
// 根据用户名字获取密码
public String getPassword(String userName){
String sql="select password from user where name= ?";
try(
Connection c=getConnection();
PreparedStatement ps = c.prepareStatement(sql);
) {
ps.setString(1,userName);
ResultSet rs = ps.executeQuery();
while(rs.next()){
return rs.getString("password");
}
}catch(SQLException e){
e.printStackTrace();
}
return null;
}
// 根据用户名获取角色
public Set<String> listRoles(String userName){
// 创建一个存储角色的集合
Set<String> roles=new HashSet<>();
String sql="select r.name from `user` u " +
"left join user_role ur on u.id=ur.uid " +
"left join role r on ur.rid=r.id " +
"where u.name= ?";
try (
Connection c = getConnection();
PreparedStatement ps=c.prepareStatement(sql);
){
ps.setString(1,userName);
ResultSet rs = ps.executeQuery();
while (rs.next()){
roles.add(rs.getString(1));
}
}catch (SQLException e){
e.printStackTrace();
}
return roles;
}
// 根据用户名获取权限
public Set<String> listPermissions(String userName){
Set<String> permissions = new HashSet<>();
String sql="select p.name from `user` u " +
"left join user_role ur on u.id=ur.uid " +
"left join role_permission rp on ur.rid=rp.rid " +
"left join permission p on rp.pid=p.id " +
"where u.name=? ";
try(
Connection c = getConnection();
PreparedStatement ps=c.prepareStatement(sql);
){
ps.setString(1,userName);
ResultSet rs = ps.executeQuery();
while (rs.next()){
permissions.add(rs.getString(1));
}
}catch(SQLException e){
e.printStackTrace();
}
return permissions;
}
/*
* 测试是否查询出数据
* */
public static void main(String[] args) {
System.out.println(new DAO().listRoles("zhang3"));
System.out.println(new DAO().listRoles("li4"));
System.out.println(new DAO().listPermissions("zhang3"));
System.out.println(new DAO().listPermissions("li4"));
}
}
测试一下,是否可以查询出数据,如下:
然后就是集成一个类,这个类属于shiro的类,类似于HttpServlet,该类的两个方法,一个是身份验证doGetAuthenticationInfo,另一个是权限授予doGetAuthenticationInfo
如下:
package com.test;
import com.test.dao.DAO;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.Set;
/**
* 这个类继承了AuthorizingRealm
* 类似于继承HttpServlet
* 里面的方法是我们自己提供,但是调用是shiro去调用
*/
public class DataBaseRealm extends AuthorizingRealm {
/*
* 授权,即权限验证
* */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principalCollection) {
/*
* PrincipalCollection是一个身份集合
* */
// 获取通过doGetAuthenticationInfo认证的用户名
String userName = (String) principalCollection.getPrimaryPrincipal();
// 根据认证的用户名,在DAO类中获取角色和权限
Set<String> permissions = new DAO().listPermissions(userName);
Set<String> roles = new DAO().listRoles(userName);
// 将获取到的角色和权限,放到能够授权的类中,即授权对象
SimpleAuthorizationInfo sai=new SimpleAuthorizationInfo();
sai.setStringPermissions(permissions);
sai.setRoles(roles);
return sai;
}
/*
* 获取
* 身份认证功能,也就是登录
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取账号密码
UsernamePasswordToken t= (UsernamePasswordToken) authenticationToken;
String userName = t.getPrincipal().toString();
String password= new String( t.getPassword());
// 获取数据库中的密码
String passwordDB = new DAO().getPassword(userName);
// 为空账号不存在,不相同的话密码错误,抛出异常,但是不报具体的错误
if(null==passwordDB||!password.equals(passwordDB)){
throw new AuthenticationException();
}
// 将通过认证的账号密码存放到认证信息中,即SimpleAuthenticationInfo
// getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
SimpleAuthenticationInfo sai = new
SimpleAuthenticationInfo(userName, password, getName());
return sai;
}
}
在这两个方法中,分别有两个参数,其为类:
AuthenticationToken :用户提交的身份和凭据,如账号密码
PrincipalCollection:用户的身份集合
其中AuthenticationToken参考的博文为:
第六章 Realm及相关对象(二) AuthenticationToken
PrincipalCollection类参考的博文为:
第六章 Realm及相关对象(四) PrincipalCollection
之后再配置shiro.ini的文件,让shiro取Realm的数据,在数据库中去取,如下:
#数据库作为数据源内容
#指定数据源,默认是情况是找IniRealm中的信息
[main]
databaseRealm=com.test.DataBaseRealm
securityManager.realms=$databaseRealm
最后在TestShiro中,要修改一个方法。。。。
老实说,应该不用修改的!!!但是我一直运行不出来,百度了好久也没有解决。。。只能修改方法,即获取当前用户这个方法,原有的方法,如下:
/*
* 获取到当前用户,注意这个对象所属的包是
* org.apache.shiro.subject.Subject;
* */
private static Subject getSubject(){
// 创建默认的安全管理实例
DefaultSecurityManager manager=new DefaultSecurityManager();
// 设置安全管理的配置文件,使用iniRealm去管理这个配置文件
Realm iniRealm=new IniRealm("classpath:shiro.ini");
// 设置该认证,也可以理解为域,安全数据源
manager.setRealm(iniRealm);
// 将安全管理放入全局对象
SecurityUtils.setSecurityManager(manager);
// 全局对象通过安全管理者生成Subject对象
Subject subject = SecurityUtils.getSubject();
return subject;
}
这个方法照理来说,没有问题的,像之前的例子都没有问题。。。。。但是用这个方法,就是无法读取到shiro.ini的数据直指向数据库。。。也不晓得为啥。。。
然后换个工厂方法获取配置文件,如下:
private static Subject getSubject() {
//加载配置文件,并获取工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取安全管理者实例
SecurityManager sm = factory.getInstance();
//将安全管理者放入全局对象
SecurityUtils.setSecurityManager(sm);
//全局对象通过安全管理者生成Subject对象
Subject subject = SecurityUtils.getSubject();
return subject;
}
之后再运行TestShiro,因为跟原来的数据源配置一样,所以没有变化,如下:
shiro作为一个安全管理的框架,最为重要的就是加密了;
一般都是使用md5的加密方式;注意了,使用md5是一种不可逆的加密方式,也就是说加密之后,无法变为原样。。。。教程是这样,但是世界之大无奇不有,到底能不能变回来,也不一定,我觉得可以使用穷举法。。。算了,这里不展开;
上代码,使用md5生成不可逆的密码,如下:
/*
* 使用md5生成不可逆的密码
* */
@Test
public void testMd5(){
String password="123";
String encodedPassword = new Md5Hash(password).toString();
// 将加密后的md5密码,再次进行加密
String encodedPassword2 = new Md5Hash(encodedPassword).toString();
System.out.println("使用shiro中的md5加密一次之后: "+encodedPassword);//结果是202cb962ac59075b964b07152d234b70
System.out.println("使用shiro中的md5加密两次之后: "+encodedPassword2);//结果是d9b1d7db4cd6e70935368a1efb10e377
}
正如上面所说的,不可逆也不一定;我还是找到了一个网站,去解密这个字符串,并且成功了。。。。并且支持其它类型的解密,该CMD5解密如下:
无论是加密一次的还是两次的都可以,三次的也可以。。但是需要注册,到这就没有折腾了。。。。
shiro,作为一个安全管理框架,当然不值那一点功能,不然要它何用,其还可以加盐。。。。盐是啥?
盐就是一个随机数。。。。
如下代码:
@Test
public void testMd5Salt(){
// 定义原始密码
String password="123";
// 生成一个盐,也就是随机数。。。
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
// 定义加密的次数
int times=2;
// 定义加密的方式
String algorithmName="md5";
// 加密.可以打开这个类,构造方法有四种
String encodedPassword = new SimpleHash(algorithmName, password, salt, times).toString();
System.out.printf("原始密码是: %s\n加的盐是: %s\n" +
"加密的次数是: %s\n加密后的密码是: %s\n",password,salt,times,encodedPassword);
}
运行如下:
注意这个加入随机数后的密码,使用上面提到的网站是无法破解的;
最终还是得跟数据库交互的;
先在数据库中添加一个字段,就是盐,其实还可以添加一个加密次数的,但是这里未添加,添加语句如下:
alter table `user`
add salt varchar(100);
然后在DAO中添加一个方法,该方法注册用户的同时将用户的密码进行md5加盐的加密,如下:
public boolean createUser(String name,String password){
String sql="insert into user values(null,?,?,?)";
// 生成一个随机数,也就是盐
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
// 定义加密方式
String algorithmName="md5";
// 定义加密的次数
int times=2;
// 将注册的密码加密,使用md5算法
String encodedPassword = new SimpleHash(algorithmName, password,
salt, times).toString();
boolean execute=false;
try(
Connection c = getConnection();
PreparedStatement ps = c.prepareStatement(sql);
){
ps.setString(1,name);
ps.setString(2,encodedPassword);
ps.setString(3,salt);
execute= ps.execute();
}catch (SQLException e){
e.printStackTrace();
}
return execute;
}
还要在DAO中添加一个根据用户名获取用户信息的方法,为了让在DataBaseRealm类中登录的时候,验证账号密码用,如下:
// 获取到用户信息
public User getUser(String userName){
User user=null;
String sql="select * from user where name=? ";
try(
Connection c = getConnection();
PreparedStatement ps = c.prepareStatement(sql);
){
ps.setString(1,userName);
ResultSet rs = ps.executeQuery();
if(rs.next()){
user=new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setSalt(rs.getString("salt"));
}
}catch (SQLException e){
e.printStackTrace();
}
return user;
}
之后修改在DataBaseRealm中的身份认证功能方法,将获取到的登录密码,以及配合数据库中的盐值进行加盐比较,判断是否跟数据库中存储的密码相等,如下:
/*
* 获取
* 身份认证功能,也就是登录
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取登录的账号密码
UsernamePasswordToken t= (UsernamePasswordToken) authenticationToken;
String userName = t.getPrincipal().toString();
String password= new String(t.getPassword());
/*
* 未加密的密码验证
* */
// 获取数据库中的密码
// String passwordDB = new DAO().getPassword(userName);
/*
* 已加密的密码验证
* */
// 获取数据库中的密码
String passwordDB = new DAO().getPassword(userName);
User user = new DAO().getUser(userName);
String salt = user.getSalt();
// 获取到登录密码加密后的结果
String encodedPassword = new SimpleHash("md5", password, salt, 2).toString();
// 为空账号不存在,不相同的话密码错误,抛出异常,但是不报具体的错误
if(null==user||!encodedPassword.equals(passwordDB)){
throw new AuthenticationException();
}
// 将通过认证的账号密码存放到认证信息中,即SimpleAuthenticationInfo
// getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
SimpleAuthenticationInfo sai = new
SimpleAuthenticationInfo(userName, password, getName());
return sai;
}
之后在TestShiro中测试,是否能够登录成功;
当然得先注册一个用户,如下:
public static void main(String[] args) {
// 注册一个用户
/* new DAO().createUser("wang5","123");
System.out.println("注册成功");*/
// 然后再验证是否能够登录
User u=new User("wang5","123");
if(login(u)){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
}
前面的将登录的账户密码跟数据库中的账号密码的对比,也就是认证是由我们自己实现的,在DataBaseRealm中自己实现的,可以让shiro帮我们实现,其实现密码的认证使用到的是CredentialsMatcher接口,在我现在使用的1.3.2的shiro中,实现这个接口有三个:SimpleCredentialsMatcher;
HashedCredentialsMatcher;这个类是SimpleCredentialsMatcher子类
PasswordMatcher;
作为一个初学者。。。。。
使用的是HashedCredentialsMatcher;
至于这三个拿来干嘛的,以及关系,这里不深究;
有一篇是说:
SimpleCredentialsMatcher和HashedCredentialsMatcher的区别关系的,可参考;
话不多说,修改上面的例子,将DataBaseRealm中的身份认证方法,修改,如下:
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取登录的账号密码
UsernamePasswordToken t= (UsernamePasswordToken) authenticationToken;
String userName = t.getPrincipal().toString();
/*
* 未加密的密码验证
* */
// 获取数据库中的密码
// String passwordDB = new DAO().getPassword(userName);
/*
* 已加密的密码验证方式二
* 通过使用shrio.ini里面配置的
* HashedCredentialsMatcher替我们验证
* */
// 获取数据库中的用户信息
User user = new DAO().getUser(userName);
String passwordDB = user.getPassword();
String salt = user.getSalt();
// 将通过认证的账号密码存放到认证信息中,即SimpleAuthenticationInfo
// getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
SimpleAuthenticationInfo sai = new
SimpleAuthenticationInfo(userName, passwordDB, ByteSource.Util.bytes(salt),getName());
return sai;
}
也就是传递参数给SimpleAuthenticationInfo 这个类,其具体的实现是要在shiro.ini配置的;
我们先对比一下,不使用的shiro帮我们认证的时候,传递给SimpleAuthenticationInfo 这个类的差别,如下:
//不使用,如下:
SimpleAuthenticationInfo sai = new
SimpleAuthenticationInfo(userName, password, getName());
//使用,如下:
SimpleAuthenticationInfo sai = new
SimpleAuthenticationInfo(userName, passwordDB, ByteSource.Util.bytes(salt),getName());
多给了其一个参数,盐值
之后配置shiro.ini,如下:
# 使用shiro自动帮我们验证密码
[main]
#指定Hashed认证配置的名字
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#加密方式
credentialsMatcher.hashAlgorithmName=md5
#加密次数
credentialsMatcher.hashIterations=2
#存储散列后的密码是否为16进制
credentialsMatcher.storedCredentialsHexEncoded=true
databaseRealm=com.test.DataBaseRealm
databaseRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$databaseRealm
配置HashedCredentialsMatcher这个类,有三种方式,具体参考博客如下:
Shiro 之 HashedCredentialsMatcher 认证匹配