学习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)[0]; 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[0]; salt = queryresults[1]; break; case external: password = getpasswordforuser(conn, username)[0]; 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所得,大家如果有什么补充或者建议,麻烦在评论区留言,谢谢!
上一篇: Selenium工具爬取商品