昨天讲了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第二天的更多相关文章

  1. RabbitMQ学习总结 第二篇:快速入门HelloWorld

    目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...

  2. 学习KnockOut第二篇之Counter

                                                                        学习KnockOut第二篇之Counter        欲看此 ...

  3. 《DOM Scripting》学习笔记-——第二章 js语法

    <Dom Scripting>学习笔记 第二章 Javascript语法 本章内容: 1.语句. 2.变量和数组. 3.运算符. 4.条件语句和循环语句. 5.函数和对象. 语句(stat ...

  4. The Road to learn React书籍学习笔记(第二章)

    The Road to learn React书籍学习笔记(第二章) 组件的内部状态 组件的内部状态也称为局部状态,允许保存.修改和删除在组件内部的属性,使用ES6类组件可以在构造函数中初始化组件的状 ...

  5. JavaWeb学习总结第二篇--第一个JavaWeb程序

    JavaWeb学习总结第二篇—第一个JavaWeb程序 最近我在学院工作室学习并加入到研究生的项目中,在学长学姐的带领下,进入项目实践中,为该项目实现一个框架(用已有框架进行改写).于是我在这里记录下 ...

  6. Shiro learning - 入门学习 Shiro中的基础知识(1)

    Shiro入门学习 一 .什么是Shiro? 看一下官网对于 what is Shiro ? 的解释 Apache Shiro (pronounced “shee-roh”, the Japanese ...

  7. [HeadFrist-HTMLCSS学习笔记]第二章深入了解超文本:认识HTML中的“HT”

    [HeadFrist-HTMLCSS学习笔记]第二章深入了解超文本:认识HTML中的"HT" 敲黑板!!! 创建HTML超链接 <a>链接文本(此处会有下划线,可以单击 ...

  8. TensorFlow 深度学习中文第二版·翻译完成

    原文:Deep Learning with TensorFlow Second Edition 协议:CC BY-NC-SA 4.0 不要担心自己的形象,只关心如何实现目标.--<原则>, ...

  9. Spring学习笔记 - 第二章 - 注解开发、配置管理第三方Bean、注解管理第三方Bean、Spring 整合 MyBatis 和 Junit 案例

    Spring 学习笔记全系列传送门: Spring学习笔记 - 第一章 - IoC(控制反转).IoC容器.Bean的实例化与生命周期.DI(依赖注入) [本章]Spring学习笔记 - 第二章 - ...

随机推荐

  1. Mysql—主从复制

    https://www.jb51.net/article/137925.htm https://blog.51cto.com/13706760/2171361 https://www.cnblogs. ...

  2. Linux:quota磁盘配额设置

    磁盘配额的概念 quota 磁盘配额功能只在指定的文件系统(分区)内有效,未设置配额的文件系统不受限制. quota 针对指定的用户账号.组账号进行限制,其他用户或组不受影响. 磁盘配额可以进行两方面 ...

  3. c# 第24节 分部方法

    本节内容: 1:分部方法 1:分部方法 2:实现分部方法 实现:

  4. [C1W3] Neural Networks and Deep Learning - Shallow neural networks

    第三周:浅层神经网络(Shallow neural networks) 神经网络概述(Neural Network Overview) 本周你将学习如何实现一个神经网络.在我们深入学习具体技术之前,我 ...

  5. luoguP2597 [ZJOI2012]灾难

    题意 这题思路好奇怪啊 见到有向无环图显然是要拓朴排序,不妨按照被吃向吃连边,那么\(x\)灭绝当且仅当x的入点都灭绝,于是考虑怎样x的入点都灭绝 比如4号节点,它灭绝当且仅当2和3灭绝,2和3灭绝当 ...

  6. C++ string push_back()

    函数功能: 在后面添加一项 vector头文件的push_back函数,在vector类中作用为在vector尾部加入一个数据.string中的push_back函数,作用是字符串之后插入一个字符. ...

  7. 使用threaddump-analyzer 快速查看jvm thread 状态信息

    日常开发中,我们可以需要通过thread dump 查看线程信息,比如锁,spotify 团队提供了一个web 界面,很方便 以下是简单使用,同时添加了docker 支持 添加docker 支持 cl ...

  8. mq代替db

    系统有个很严重的性能问题,法国人浪费了半年多都没有解决,他们试图从sql的角度分析哪里能有改善,大方向错了,再努力也没用. 我接手以后,也走了点弯路,一上手觉得肯定能用cache解决问题,结果cach ...

  9. 调试九法: 软硬件错误的排查之道 (David J. Agans 著)

    第1章 简介 (已看) 第2章 总体规则 (已看) 第3章 理解系统 (已看) 第4章 制造失败 第5章 不要想, 而要看 第6章 分而治之 第7章 一次只改一个地方 第8章 保持审计跟踪 第9章 检 ...

  10. AChartEngine折线图实例

    最近做项目要用到图表,在网上找相关的解决方案找了很久,搜到最多的就是这个框架,所以就开始研究下怎么使用,首先研究的就是折线图,如是做了一个实例. AChartEngine下载地址:http://cod ...