第一节的时候我介绍过,shiro有很多加密算法,如md5和sha,而且还支持加盐,使得密码的解析变得更有难度,更好的保障了数据的安全性。

这里我们要介绍的是md5算法,因为比较常用。首先我们来看看md5算法的各种实现方式:

package com.wujianwu.test;

import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash; public class TestMd5 { public static void main(String[] args) {
String password = "123456";//要加密的字符串
String salt = "wjw";//盐
Integer hashIterations = 2;//散列次数
//1.不加盐的md5
Md5Hash md5 = new Md5Hash(password);
System.out.println(md5.toString()); //2.加盐的md5
md5 = new Md5Hash(password, salt);
System.out.println(md5.toString()); //3.加盐再设置散列次数的md5
md5 = new Md5Hash(password, salt, hashIterations);
System.out.println(md5.toString()); //4.利用SimpleHash来设置md5(上面三种都可以通过这个来设置,这里举例加盐加散列次数的)
//第一个参数是算法名称,这里指定md5,第二个是要加密的密码,第三个参数是加盐,第四个是散列次数
SimpleHash hash = new SimpleHash("md5", password, salt,hashIterations);
System.out.println(hash.toString());
}
}

上面列举了md5算法的各种实现,包括不加盐的,加盐的,加盐加散列次数的(从HashedCredentialsMatcher的源码可得知,默认散列次数为1),还有通过SimpleHash来实现md5的方式,下面看看它们的输出:

输出信息:

e10adc3949ba59abbe56e057f20f883e
7ca5a3bfd6fc151442219490509cb4d8
3d80e8a34ae898c07d3fb237bae26b7d
3d80e8a34ae898c07d3fb237bae26b7d

可以看到,不加盐、加盐、加盐加散列次数后的加密字段是不一样的,而它们的数据安全性也是递增的!但是注意散列次数不能设置过大,否则运行效率会变低。下面讲讲加密算法在realm中的应用。

一般我们在数据库保存的用户密码都是经过加密后的密码,所以我们想把加密后的用户信息传给shiro进行认证,就必须把该密码加密的算法、添加的盐以及散列的次数告诉shiro,因此我们需要配置realm里面的凭证匹配器credentialsMatcher,当用户将账号密码输进来时,shiro就会根据我们设置的加密规则对密码进行加密加盐,然后与realm中查询封装好的数据库数据进行比对认证。

public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {

    //TODO - complete JavaDoc

    private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class);

    private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();

    /**
* The default suffix appended to the realm name used for caching authentication data.
*
* @since 1.2
*/
private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authenticationCache"; /**
* Credentials matcher used to determine if the provided credentials match the credentials stored in the data store.
*/
private CredentialsMatcher credentialsMatcher;

下面我们开始配置凭证匹配器:

shiro.ini:

[main]

#配置散列算法,用于用户输入身份和密码后进行加密然后与数据库数据比对
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/test
dataSource.user=root
dataSource.password=root
myRealm=com.wujianwu.realm.MyRealm
myRealm.credentialsMatcher=$credentialsMatcher
myRealm.dataSource=$dataSource
securityManager.realm=$myRealm

我们设置了一个凭证匹配器HashedCredentialsMatcher,然后再设置它里面的相关属性,如算法名称为md5,散列次数是两次,然后将设置好的凭证匹配器赋予我们自定义的realm,再修改一下我们的realm的部分代码,将数据库中的盐查询出来,并调用SimpleAuthenticationInfo的带有盐参数的构造方法来封装数据库查出来的用户数据(其实就是告诉shiro要比对的是加密后的信息),以下为新的myRealm:

package com.wujianwu.realm;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import javax.sql.DataSource; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource; import com.wujianwu.bean.User; public class MyRealm extends AuthorizingRealm{ private DataSource dataSource;
@Override
public String getName() {
// TODO Auto-generated method stub
return "myRealm";
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = (String) token.getPrincipal();
SimpleAuthenticationInfo info = null;
User user = getUserInfo(userName);
System.out.println("用户加密后的密码"+user.getPassword()+"==============");
info = new SimpleAuthenticationInfo(userName, user.getPassword(), ByteSource.Util.bytes(user.getPasswordSalt()), getName());
return info;
} @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
// TODO Auto-generated method stub
return null;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
} /**
* 根据用户名从数据库查询出用户信息
* @param username
* @return
*/
private User getUserInfo(String username){
User user = new User();
String sql = "select username,password,password_salt from users where username=?";
Connection connection = null;
PreparedStatement statement = null;
ResultSet set = null;
try {
connection = dataSource.getConnection();
statement = connection.prepareStatement(sql);
statement.setString(1, username);
set = statement.executeQuery(); if(set.next()) {
String username1 = set.getString(1);
String password = set.getString(2);
String salt = set.getString(3);
user.setPassword(password);
user.setUsername(username1);
user.setPasswordSalt(salt);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
closeAll(connection, set, statement);
}
return user;
} /**
* 关闭资源
* @param conn
* @param set
* @param statement
*/
private void closeAll(Connection conn,ResultSet set,PreparedStatement statement) {
try {
if(set != null) {
set.close();
}
if(statement != null) {
statement.close();
}
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

注意SimpleAuthenticationInfo构造函数中的盐是ByteSource类型的,因此我们需要用接口ByteSource中的静态内部类Util下的bytes(String str)方法来将我们的盐转换成ByteSource类型。

设置好凭证匹配器,改好自定义的Realm,下面我们开始测试,还是熟悉的流程:

package com.wujianwu.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class TestMyRealm { private static final Logger logger = LoggerFactory.getLogger(TestMyRealm.class);
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123456");
try {
subject.login(token);
if(subject.isAuthenticated()) {
logger.info("用户登录认证成功");
}
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
logger.error("用户名或者密码错误,登录失败");
}
}
}

数据库的数据:

运行完控制台输出如下:

2019-07-28 19:59:30,535 INFO [com.mchange.v2.log.MLog] - MLog clients using slf4j logging.
2019-07-28 19:59:30,963 INFO [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
2019-07-28 19:59:31,055 INFO [org.apache.shiro.config.IniSecurityManagerFactory] - Realms have been explicitly set on the SecurityManager instance - auto-setting of realms will not occur.
2019-07-28 19:59:31,085 INFO [com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource] - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 2zm2h7a4fnv3mn329o49|180bc464, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 2zm2h7a4fnv3mn329o49|180bc464, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/test, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
用户加密后的密码3d80e8a34ae898c07d3fb237bae26b7d==============
2019-07-28 19:59:31,399 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
2019-07-28 19:59:31,405 INFO [com.wujianwu.test.TestMyRealm] - 用户登录认证成功

可以看到,我们模拟的用户输入的账号是zhangsan,密码是123456,但数据库中的密码是加密加盐后的密码,所以我们给realm配置了加密算法的规则,让它将我们传过去的密码进行了同样的加密加盐(这里盐不需要我们设置,是从数据库中查询出来的),然后再和数据库的数据进行比对认证,因此这里认证是成功的(123456进行md5加密和wjw加盐后然后再散列2次就是这一串东西了:3d80e8a34ae898c07d3fb237bae26b7d,至于内部如何实现,有兴趣的大佬可以去看一下《算法》这本书= =)

以上就是散列算法(加密算法)在shiro中的使用Demo,如果有什么补充或者修改的请在评论区留言,谢谢!

shiro加密算法的更多相关文章

  1. 浅谈Shiro框架中的加密算法,以及校验

    在涉及到密码存储问题上,应该加密/生成密码摘要存储,而不是存储明文密码.为什么要加密:网络安全问题是一个很大的隐患,用户数据泄露事件层出不穷,比如12306账号泄露. Shiro提供了base64和1 ...

  2. Apache Shiro系列(1)

    Apache Shiro是啥呢,安全框架. 360百科是这么描述的:        Apache Shiro(日语"堡垒(Castle)"的意思)是一个强大易用的Java安全框架, ...

  3. Apache Shiro系列之五,概述 —— 配置

    Shiro设计的初衷就是可以运行于任何环境:无论是简单的命令行应用程序还是复杂的企业集群应用.由于运行环境的多样性,所以有多种配置机制可用于配置,本节我们将介绍Shiro内核支持的这几种配置机制.   ...

  4. Apache Shiro系列二,概述 —— 基本概念

    做任何事情,首先要双方就一些概念的理解达成一致,这样大家就有共同语言,后续的沟通效率会高一些. #,Authentication,认证,也就是验证用户的身份,就是确定你是不是你,比如通过用户名.密码的 ...

  5. Apache Shiro系列一,概述 —— 初识

    一.什么是Shiro Apache Shiro是一个强大.灵活.开源的安全框架,它支持用户认证.权限控制.企业会话管理以及加密等. Apache Shiro的第一个也是最重要的一个目标就是易于使用和理 ...

  6. 30分钟学会如何使用Shiro

    本篇内容大多总结自张开涛的<跟我学Shiro>原文地址:http://jinnianshilongnian.iteye.com/blog/2018936 我并没有全部看完,只是选择了一部分 ...

  7. Apache Shiro 简单概念

    Apache Shiro 是ASF旗下的一款开源软件(Shiro发音为"shee-roh",日语"堡垒(Castle)"的意思),提供了一个强大而灵活的安全框架 ...

  8. 让Apache Shiro保护你的应用

    在尝试保护你的应用时,你是否有过挫败感?是否觉得现有的Java安全解决方案难以使用,只会让你更糊涂?本文介绍的Apache Shiro,是一个不同寻常的Java安全框架,为保护应用提供了简单而强大的方 ...

  9. Java开源安全框架之Apache Shiro

    APACHE SHIRO安全框架 1      背景 Shiro项目始于2003年初,当时它叫JSecurity项目,当时对于Java应用开发人员没有太多的安全替代方案,始终被一个叫JAAS(Java ...

随机推荐

  1. 安装新版Magisk卡在启动页面等问题以及解决办法

      手持小米9,之前更新了新版本的Magisk Manager之后,发现手机再打开会一直卡在进入界面无法启动(如下图显示)   逛了逛Github.XDA.Stack Overflow等论坛,大致结论 ...

  2. NSURLSession中的downloadTask的使用

    1.用downloadTask下载图片 优点:简单 缺点:不能监听下载的进度 代码示例: NSURL *url = [NSURL URLWithString:@"http://pic1.wi ...

  3. 使用JAVAScript技术在WEB网页实现摇一摇的应用

    实现效果如下: 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  4. man -k, man -f : nothing appropriate ; 更新 whatis 数据库

    man 有两个选项: -f, –whatis Equivalent to whatis. Display a ) for details. -k, –apropos Equivalent to apr ...

  5. C++学习三 模板类出错总结(Missing template arguments before 'L')

    一.模板类的说明 模板类有一个好处是可以放宽你输入的数据类型. 比如有这样的一个函数: int add(int x, int y) { return x+y; } 这个函数对于int类型的x,y才适合 ...

  6. python session保持登录,新增地址,并删除,由观察可知,address_id决定删除的内容;

    import requests,reheaders={"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) Ap ...

  7. tensorflow的MNIST教程

    (ps:根据自己的理解,提炼了一下官方文档的内容,错误的地方希望大佬们多多指正.....) 0x01:数据集的获取和表示 数据集的获取,可以通过代码自动下载.这里的数据就是各种手写数字图片和图片对应的 ...

  8. 读取只包含标签的xml

    什么是XML XML是可扩展标记语言(Extensible Markup Language)的缩写,其中标记是关键部分.用户可以创建内容,然后使用限定标记标记它,从而使每个单词.短语或块成为可识别.可 ...

  9. 2019 SDN阅读作业(2)

    1.过去20年中可编程网络的发展可以分为几个阶段?每个阶段的贡献是什么? 可编程网络的发展可以分为以下三个阶段: (1)主动网络(Active networking,20世纪90年代中期到21世纪初) ...

  10. Luogu P5363 [SDOI2019]移动金币

    话说这题放在智推里好久了的说,再不写掉对不起自己233 首先你要知道一个叫做阶梯Nim的东西,具体的可以看这篇博客 那么我们发现这和这道题的关系就很明显了,我们把两个金币之间的距离看作阶梯Nim的每一 ...