第一节的时候我介绍过,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. [b0007] windows 下 eclipse 开发 hdfs程序样例

    目的: 学习使用hdfs 的java命令操作 相关: 进化: [b0010] windows 下 eclipse 开发 hdfs程序样例 (二) [b0011] windows 下 eclipse 开 ...

  2. 渗透测试学习 十八、 XSS跨站脚本漏洞详解 续

    XSS平台搭建 ISS+zkeysphp 首先在IIS中新建一个网站,详细过程可以看我之前写搭环境的文章 (下面的写入选项不需要选中) 安装ZKEYS PHP 修改数据库配置 用户名:root 密码: ...

  3. itest(爱测试) 4.0.0 发布,开源敏捷测试管理 &BUG 跟踪管理软件

      v4.0.0 下载地址 :itest下载 itest 简介:查看简介 V4.0.0 根据用户反馈,共增加了29个更新:其中有15 个功能增强和14个BUG修复 :UI更加清爽,用例执行和用例复制粘 ...

  4. to_sql, to_csv, to_excel, to_json

    1.to_sql def to_sql(self, name, con, schema=None, if_exists="fail", index=True, index_labe ...

  5. 2019.6.11_MySQL进阶二:主键与外键

    通过图形界面(UI)创建外键 打开设计表,在对应的栏位填写相应的内容.其中FK_deptno是限制名 # 先给主表建立主键   ALTER TABLE dept ADD PRIMARY KEY(dep ...

  6. 新建全色或者resize(毫无价值,只是做记录)

    import glob import os,sys import shutil import numpy as np import cv2 import matplotlib.pyplot as pl ...

  7. CF1252J Tiling Terrace

    CF1252J Tiling Terrace 洛谷评测传送门 题目描述 Talia has just bought an abandoned house in the outskirt of Jaka ...

  8. C++面向对象程序设计学习笔记(6)

    多态性 编译时的多态性与运行时的多态性 在面向对象方法中,所谓多态性就是不同对象收到相同信息时,产生不同的行为.在c++程序设计中,即"一个接口,多种方法" 在C++中,多态性的实 ...

  9. VScode Python 虚拟环境

    安装python环境 在VScode里设置Python 虚拟环境查找路径(Settings -> Extensions -> Python Configurations -> Env ...

  10. CF-1208 C.Magic Grid

    题目 大意:构造一个n行n列的矩阵,使得每一行,每一列的异或和都相等,n是4的倍数. 先看4*4的矩阵,我们很容易构造出符合要求的矩阵,比如 0    1    2    3 4    5    6  ...