当前位置: 移动技术网 > IT编程>开发语言>Java > 学习shiro第二天

学习shiro第二天

2019年07月26日  | 移动技术网IT编程  | 我要评论

昨天讲了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所得,大家如果有什么补充或者建议,麻烦在评论区留言,谢谢!

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网