学习shiro第二天
昨天讲了shiro的认证流程以及代码实现,今天将对这个进行扩展。
因为我们的测试数据是shiro.ini文件中配置的静态数据,但实际上数据应该从数据库中查询出来才合理,因此我们今天讲讲JdbcRealm的使用。
本次需要的jar包如下:
commons-beanutils-1.9.3.jar
commons-logging-1.2.jar
jcl-over-slf4j-1.7.12.jar
log4j-1.2.16.jar
shiro-all-1.4.1.jar
slf4j-api-1.7.25.jar
slf4j-log4j12-1.6.4.jar
mysql-connector-java-5.1.47.jar
mchange-commons-java-0.2.11.jar
c3p0-0.9.5.2.jar
IniSecurityManagerFactory部分源码:
public static final String MAIN_SECTION_NAME = "main";
public static final String SECURITY_MANAGER_NAME = "securityManager";
public static final String INI_REALM_NAME = "iniRealm";
......
protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
Map<String, Object> defaults = new LinkedHashMap<String, Object>();
SecurityManager securityManager = createDefaultInstance();
defaults.put(SECURITY_MANAGER_NAME, securityManager);
if (shouldImplicitlyCreateRealm(ini)) {
Realm realm = createRealm(ini);
if (realm != null) {
defaults.put(INI_REALM_NAME, realm);
}
}
......
从这段源码可以看到,IniSecurityManagerFactory在初始化时默认将iniRealm设置到SecurityManager中,除非我们自己设置SecurityManager使用哪个realm,它获取判断realm不为空,才将我们设置的realm设置到SecurityManager中,因此我们要在shiro.ini中配置新的realm即JdbcRealm。
JdbcRealm部分源码:
/**
* 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 = ?"; ......
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
......
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername(); // Null username is invalid
if (username == null) {
throw new AccountException("Null usernames are not allowed by this realm.");
} Connection conn = null;
SimpleAuthenticationInfo info = null;
try {
conn = dataSource.getConnection(); String password = null;
String salt = null;
switch (saltStyle) {
case NO_SALT:
password = getPasswordForUser(conn, username)[];
break;
case CRYPT:
// TODO: separate password and hash from getPasswordForUser[0]
throw new ConfigurationException("Not implemented yet");
//break;
case COLUMN:
String[] queryResults = getPasswordForUser(conn, username);
password = queryResults[];
salt = queryResults[];
break;
case EXTERNAL:
password = getPasswordForUser(conn, username)[];
salt = getSaltForUser(username);
} if (password == null) {
throw new UnknownAccountException("No account found for user [" + username + "]");
} info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName()); if (salt != null) {
info.setCredentialsSalt(ByteSource.Util.bytes(salt));
} } catch (SQLException e) {
final String message = "There was a SQL error while authenticating user [" + username + "]";
if (log.isErrorEnabled()) {
log.error(message, e);
} // Rethrow any SQL errors as an authentication exception
throw new AuthenticationException(message, e);
} finally {
JdbcUtils.closeConnection(conn);
} return info;
}
从JdbcRealm的源码中我们看到了许多sql语句,都是从表users中去查询字段username、password、password_salt等等,而且源码中还出现了dataSource,而这个dataSource是需要我们set进去的,因此要使用JdbcRealm,我们必须在数据库创建表users以及对应的字段,还需要为JdbcRealm注入dataSource。下面我们开始配置shiro.ini文件。
shiro.ini文件:
[main]
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=org.apache.shiro.realm.jdbc.JdbcRealm
myRealm.dataSource=$dataSource
securityManager.realm=$myRealm
差点忘了说,在shiro.ini文件中,有许多模块组成,比如昨天的[users]用来存放静态数据,和上面的[main]是用来配置SecurityManager和它所需要的依赖组件如realm的模块,当然还有配置用户角色的模块[role],我们这里就不多讲了,留到授权模块再将。
了解了[main]的应用,我们再看看上面的配置,首先我们配置了一个dataSource(这里配的是数据源c3p0,记得导包哦),包括配置它的数据库驱动、Url、数据库登录名以及密码,然后配了一个myRealm指向了JdbcRealm,再将上面的dataSource设置到myRealm中从而实现了dataSource的注入(这里的$代表注入,跟spring的注入异曲同工),再将这个myRealm设置成SecurityManager的默认realm从而完成配置。
配置完成了,接下来我们还要在数据库中创建users表:

如图,在test数据库下创建users表,其中必须包含JdbcRealm类中的sql语句中出现的字段,这里我们设置了三个。
创建完表,我们开始编码,流程其实跟第一天的差不多:
package test_JdbcRealm; 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.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class TestJdbcRealm { private static final Logger logger = LoggerFactory.getLogger(TestJdbcRealm.class); public static void main(String[] args) {
//1.创建securityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.获取securityManager实例
SecurityManager securityManager = factory.getInstance();
//3.将SecurityManager实例设置到SecurityUtils工具类中
SecurityUtils.setSecurityManager(securityManager);
//4。创建Subject实例
Subject subject = SecurityUtils.getSubject();
//5.获取token,模拟用户登陆
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123456");
try {
//6.认证token
subject.login(token);
if(subject.isAuthenticated()) {
logger.info("登陆成功");
}
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
logger.error("用户名或密码错误,登陆失败");
}
}
}
2019-07-25 23:30:43,705 INFO [com.mchange.v2.log.MLog] - MLog clients using slf4j logging.
2019-07-25 23:30:44,102 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-25 23:30:44,211 INFO [org.apache.shiro.config.IniSecurityManagerFactory] - Realms have been explicitly set on the SecurityManager instance - auto-setting of realms will not occur.
2019-07-25 23:30:44,233 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 -> 2zm2h8a4bl36b813yacsm|7e0ea639, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 2zm2h8a4bl36b813yacsm|7e0ea639, 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 ]
2019-07-25 23:30:44,525 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
2019-07-25 23:30:44,537 INFO [test_JdbcRealm.TestJdbcRealm] - 登陆成功
同样是这几个步骤,但是在加载配置文件生成SecurityManager的实例却是不一样的,因为我们在配置文件中配置了SecurityManager的默认realm为JdbcRealm,因此在subjec.login(token)认证时,最终会通过JdbcRealm的认证方法从数据库中查出用户数据来进行比对认证!
总结:
1.在使用一个类的时候,我们需要通过源码去了解它需要什么,然后才能有目的地进行配置,就像JdbcRealm这个类,它需要一张表和数据源来实现从数据库查询出数据进行认证;
2.shiro.ini包含多个组成部分,如[main],[users],[role]还有[url],我们需要了解各个部分地主要功能和作用;
3.从上面地例子我们可以得知,SecurityManager使用地realm是可以进行配置的!!因此也就说明了我们可以自定义realm来实现认证和授权逻辑!这部分后续我学习到了将会进行分享。
以上是我今天学习shiro所得,大家如果有什么补充或者建议,麻烦在评论区留言,谢谢!
学习shiro第二天的更多相关文章
- RabbitMQ学习总结 第二篇:快速入门HelloWorld
目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...
- 学习KnockOut第二篇之Counter
学习KnockOut第二篇之Counter 欲看此 ...
- 《DOM Scripting》学习笔记-——第二章 js语法
<Dom Scripting>学习笔记 第二章 Javascript语法 本章内容: 1.语句. 2.变量和数组. 3.运算符. 4.条件语句和循环语句. 5.函数和对象. 语句(stat ...
- The Road to learn React书籍学习笔记(第二章)
The Road to learn React书籍学习笔记(第二章) 组件的内部状态 组件的内部状态也称为局部状态,允许保存.修改和删除在组件内部的属性,使用ES6类组件可以在构造函数中初始化组件的状 ...
- JavaWeb学习总结第二篇--第一个JavaWeb程序
JavaWeb学习总结第二篇—第一个JavaWeb程序 最近我在学院工作室学习并加入到研究生的项目中,在学长学姐的带领下,进入项目实践中,为该项目实现一个框架(用已有框架进行改写).于是我在这里记录下 ...
- Shiro learning - 入门学习 Shiro中的基础知识(1)
Shiro入门学习 一 .什么是Shiro? 看一下官网对于 what is Shiro ? 的解释 Apache Shiro (pronounced “shee-roh”, the Japanese ...
- [HeadFrist-HTMLCSS学习笔记]第二章深入了解超文本:认识HTML中的“HT”
[HeadFrist-HTMLCSS学习笔记]第二章深入了解超文本:认识HTML中的"HT" 敲黑板!!! 创建HTML超链接 <a>链接文本(此处会有下划线,可以单击 ...
- TensorFlow 深度学习中文第二版·翻译完成
原文:Deep Learning with TensorFlow Second Edition 协议:CC BY-NC-SA 4.0 不要担心自己的形象,只关心如何实现目标.--<原则>, ...
- Spring学习笔记 - 第二章 - 注解开发、配置管理第三方Bean、注解管理第三方Bean、Spring 整合 MyBatis 和 Junit 案例
Spring 学习笔记全系列传送门: Spring学习笔记 - 第一章 - IoC(控制反转).IoC容器.Bean的实例化与生命周期.DI(依赖注入) [本章]Spring学习笔记 - 第二章 - ...
随机推荐
- 双十一DIY装机记
一.装机背景 最近发现古董笔记本太卡了,用了近6年,尽管自己不打游戏,但是业余时间写写代码,同时开两个编辑器,打开个大一点的软件都卡的不行,据说更换固态硬盘可以提高速度,于是乎,京东买了一个500 ...
- 从0系统学Android-1.4日志工具的使用
更多精品文章分类 1.4 日志工具 简单介绍一下日志工具,对以后的开发非常有用 1.4.1 使用日志工具 Log Log 日志工具类提供了 5 个方法来供我们打印信息(级别逐渐提高) Log.v(): ...
- linux用户管理章节笔记
1 更改有效用户组 :newgrp zeng 把当前用户的有效用户组更改为zeng.事后可以使用groups命令查看. 2 在使用useradd命令增加用户时,在/etc/passwd的值一般会参考 ...
- 【Web】解决简书图片不显示问题“系统维护中,图片暂时无法加载”
简书不显示图片的解决方法 首次编辑于2019-6-6 最近几天在浏览简书上的文章时,发现图片显示不出来,提示"系统维护中,图片暂时无法加载". 猜测应该是简书由于某种原因暂时屏蔽了 ...
- INSTALL_FAILED_NO_MATCHING_ABIS解决办法
解决方法: 在project切换到Android状态下的build.grade(Module:app)中添加如下内容即可: splits { abi { enable true reset() inc ...
- hadoop 自定义OutputFormat
1.继承FileOutputFormat,复写getRecordWriter方法 /** * @Description:自定义outputFormat,输出数据到不同的文件 */ public cla ...
- 浅入浅出 Go 语言接口的原理
浅入浅出 Go 语言接口的原理 接口是 Go 语言的重要组成部分,它在 Go 语言中通过一组方法指定了一个对象的行为,接口 interface 的引入能够让我们在 Go 语言更好地组织并写出易于测试的 ...
- JavaScript判断是否是数字
JavaScript判断是否是数字的几种方法 isNaN() /** * 判断是否是数字 */ function isNumber(value){ if(isNaN(value)){ return f ...
- SpringBoot定时器任务
Spring Boot使用@Scheduled定时器任务 摘要: Spring Boot之使用@Scheduled定时器任务 假设我们已经搭建好了一个基于Spring Boot项目,首先我们要在A ...
- CF92B-Binary Number-(思维)
https://vjudge.net/problem/CodeForces-92B 题意:给一个长度为106的二进制数,有两种操作,第一种是除以2,第二种是末尾+1,以二进制运算,问这个二进制数最少几 ...