zoukankan      html  css  js  c++  java
  • Shrio认证详解+自定义Realm

    Authentication(身份认证)是Shiro权限控制的第一步,用来告诉系统你就是你。

    在提交认证的时候,我们需要给系统提交两个信息:

    Principals:是一个表示用户的唯一属性,可以是用户名,邮箱之类的。

    Credentials:是证明用户身份的证书,可以是密码或者指纹之类的。

    认证主要分为三步:

    1、收集认证信息

    2、提交认证信息

    3、如果认证成功,则允许访问,否则就拒绝访问或者重试。

    收集认证信息

    入门的例子中,使用了一个UsernamePasswordToken来收集用户的用户名和密码,用来登陆。

    这个类支持最简单的用户名和密码登陆。实现了org.apache.shiro.authc.AuthenticationToken接口。

    AuthenticationToken接口是认证系统的基础,只有getCredentials(),getPrincipal()两个方法用来获取基本的认证信息。

    HostAuthenticationToken和RememberMeAuthenticationToken是它的两个子接口。

    HostAuthenticationToken只有一个getHost()方法用来获取请求的地址信息。

    RememberMeAuthenticationToken只有一个isRememberMe()方法用来标记用户是否需要记住我。

    然后还有两个子类UsernamePasswordToken和CasToken提供了基本的实现。其中CasToken已经被废弃了。

    例:

    //Example using most common scenario of username/password pair:
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    //"Remember Me" built-in: 
    token.setRememberMe(true);

    提交认证信息:

    收集好认证信息之后,保存为AuthenticationToken的一个实例,我们需要提交这个认证信息来进行认证。

    进行认证前,我们首先需要获取当前用户(Subject)。然后调用login方法来进行登陆。

    例:

    Subject currentUser = SecurityUtils.getSubject();
    currentUser.login(token);

    处理结果:

    如果登陆成功的话,则不会有什么异常,此时如果调用isAuthenticated()方法,则会返回true。

    如果登陆失败的话,则被抛出异常,在SHiro中,提供了很多异常类,可以用来捕捉具体的异常。

    例子:

    try {
        currentUser.login(token);
    } catch ( UnknownAccountException uae ) { ...
    } catch ( IncorrectCredentialsException ice ) { ...
    } catch ( LockedAccountException lae ) { ...
    } catch ( ExcessiveAttemptsException eae ) { ...
    } ... catch your own ...
    } catch ( AuthenticationException ae ) {
        //unexpected error?
    }
    
    //No problems, continue on as expected...

    可以看到,前面出现了Remembered和Authenticated。那这两种有什么区别呢?

    需要注意的是,这是两种互斥的情况,当我们在登陆的时候,登陆成功之后,我们Authenticated返回的是true。

    选择Remeber me的时候,我们下次可以不登陆直接访问,而我们下次登陆之后,Remembered返回的是true,但是Authenticated返回的是false。

    在登陆之后,我们可以可以用logout()方法来注销。这时候用户的信息都会被清空,包括保存在Cookie中的RemeberMe信息还有session也会被无效。

    上面我们就简单的讲述了一下在代码中实现登陆验证的流程。具体实例可以参考入门的例子。

    那么,在Shrio内部是怎么样的一个认证流程呢?大概可以用下图来概括:

    第一步:在代码中调用login方法,传递构造好的AuthenticationToken实例。

    第二步:通过一个DelegatingSubject来分发认证请求给SecurityManager

    第三步:SecurityManager容器会把接收到的token简单的转发给它内部的认证器实例通过调用认证器的authenticate(token)方法。

    这通常是一个ModularRealmAuthenticator实例,用来支持多个Realm。

    第四步:如果有定义多个Realm则ModularRealmAuthenticator会初始化一个支持多个Realm的认证器,通过配置的AuthenticationStrategy。

    第五步:每一个配置的Realm都会被检测是否支持提交的AuthenticationToken,如果支持的话就会调用getAuthenticationInfo方法,从Realm中获取数据来跟提交的token进行验证。

    验证器

    在SecurityManager中,默认使用ModularRealmAuthenticator实例,它不仅仅支持单Realm还支持多个Realm。

    如果实在单个Realm的情况下,ModualrRealmAuthenticator会直接调用这个Realm来尝试验证。

    如果我们需要定义自己的验证器的话,可以通过在配置文件的[main]中如下定义:

    [main]
    ...
    authenticator = com.foo.bar.CustomAuthenticator
    securityManager.authenticator = $authenticator

    验证策略

    如果是只有一个Realms的时候,则不需要验证策略。

    如果是有两个以上的Realm的时候,ModularRealmAuthenticator依赖于AuthenticationStrategy组件来决定认证成功或者失败的条件,

    比如是一个成功就成功还是要都成功才是成功之类的。。。。

    在Shiro里面已经有三个认证策略的实现

    AtLeastOneSuccessfulStrategy:只要有一个或一个以上的认证成功就表示认证成功

    FirstSuccessfulStrategy;只有第一个认证成功的时候才表示认证成功

    AllSuccessfulStrategy:只有所有的都认证成功的时候才表示认证成功。

    在ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy的验证策略。当然也可以通过下面的方式定义其他的验证策略。

    [main]
    ...
    authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
    securityManager.authenticator.authenticationStrategy = $authcStrategy
    ...

    Realm认证顺序

    在定义了多个Realm的时候,如果有多个Realm支持当前的AuthenticationToken,则会依次调用Realm的getAuthenticationInfo方法。

    调用的顺序分为两种:

    隐式的:

    如果在SecurityManager中定义了如下的几个Realm,则会按照他们定义的顺序去调用。

    blahRealm = com.company.blah.Realm
    ...
    fooRealm = com.company.foo.Realm
    ...
    barRealm = com.company.another.Realm

    此时的效果就等于下面的语句。

    securityManager.realms = $blahRealm, $fooRealm, $barRealm、

    显示的:

    就如上面的一样,如果在securityManager.realms中配置的时候,改变realm的配置顺序,则会按照这个配置顺序来调用,这就是显示的配置。

    blahRealm = com.company.blah.Realm
    ...
    fooRealm = com.company.foo.Realm
    ...
    barRealm = com.company.another.Realm
    securityManager.realms = $fooRealm, $barRealm, $blahRealm
    ...

    域认证

    前面说了下Realm的认证策略和认证顺序,那么,在Realm认证的时候,究竟发生什么事情了呢?

    supporting AuthenticationTokens

    在Realm被用来尝试认证登陆的时候首先会调用supports方法,来检测是否能解析这个AuthenticationToken,决定是否进行认证。

    Handing supported AuthenticationTokens

    如果这个realm支持提交的AuthenticationTokens的话,解析器会调用Realm的getAuthenticationINfo(token)方法,来尝试认证,

    这个方法大概做了下面这些事情:

    1、检测token中的唯一用户标识

    2、基于唯一标识 去数据源中查找

    3、确保提供的证书跟数据源中保存的一样

    4、如果证书一样,就封装一个AuthenticationINfo实例返回

    5、如果证书不匹配,则抛出一个AuthenticationException异常

    下面,我们来看看Shrio提供的Realm的类结构:

    org.apache.shiro.realm.Realm(I):base
      org.apache.shiro.realm.CachingRealm(Abstract):提供缓存支持
        org.apache.shiro.realm.AuthenticatingRealm(Abstract):提供认证支持
          org.apache.shiro.realm.AuthorizingRealm(Abstract):提供授权支持
            org.apache.shiro.realm.SimpleAccountRealm(C):简单的用户名密码支持
              org.apache.shiro.realm.text.TextConfigurationRealm(C):支持Text文件的简单用户名密码支持
                org.apache.shiro.realm.text.IniRealm(C):通过INI文件的简单用户名密码支持(默认使用这个)
                org.apache.shiro.realm.text.PropertiesRealm(C):支持属性文件的简单用户名密码支持
            org.apache.shiro.realm.jdbc.JdbcRealm(C):支持通过JDBC认证

     在系统默认的情况下,系统使用的是IniRealm这个实现,可以从INI配置文件的[users]和[roles]两个节中读取用户信息和权限信息。

    如果我们需要定义自己的Realm实现的话,一般都是继承AuthorizingRealm。

    稍候,我们将简单介绍下如果从db中来实现认证。

    证书匹配

    前面我们说过,Realm需要去匹配用户提交的证书跟数据源中存储的证书是否匹配,如果匹配的话就认为是认证成功。

    在获得用户唯一标识后,系统回去Realm会去检索用户的证书,然后通过CredentialsMatcher来检测证书是否匹配。

    Shrio中也提供了一些证书的匹配器可以直接拿来使用,使用下面的方法变更默认的匹配器:

    [main]
    ...
    customMatcher = com.company.shiro.realm.CustomCredentialsMatcher
    myRealm = com.company.shiro.realm.MyRealm
    myRealm.credentialsMatcher = $customMatcher
    ...

    Simple Equality Check

    默认情况下所有提供的Realm实现都是使用SimpleCredentialsMatcher来检测证书是否匹配。

    不过一般情况下都不需要变更,因为默认的就足够了。

    Hashing Credentials

    相比使用原始的数据存储起来,拿来匹配,我们更愿意将证书加密进行存储来进行匹配。那么如何使用呢?

    在Shiro中提供了几个HashedCredentialsMatcher的子类,用来实现这个功能。包括MD5、SHA-256等等的加密方式。

    我们可以通过下面的配置方式:

    [main]
    ...
    credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
    # base64 encoding, not hex in this example:
    credentialsMatcher.storedCredentialsHexEncoded = false
    credentialsMatcher.hashIterations = 1024
    # This next property is only needed in Shiro 1.0.  Remove it in 1.1 and later:
    credentialsMatcher.hashSalted = true
    
    ...
    myRealm = com.company.....
    myRealm.credentialsMatcher = $credentialsMatcher
    ...

    需要注意的是,这种情况下在Realm的实现中需要返回一个SaltedAuthenticationInfo,而不是普通的AuthenticationInfo,因为在用户提交认证的时候,需要获取相同的salt来进行加密,进行匹配认证。

    那这个salt(盐)是用来干嘛的呢?

    这是因为在进行MD5之类加密的时候,还是可以进行破解的,但是如果加入一个变量来进行加密之后,就基本上是无法破解了(不知道这个SALT的情况下)。

    下面我们就做一个通过JDBC来认证的登陆认证程序。点此看源码

    首先我们需要一个用户表,脚本如下:

    CREATE DATABASE `db_shiro` 
    
    USE `db_shiro`;
    
    DROP TABLE IF EXISTS `users`;
    
    CREATE TABLE `users` (
      `id` int(4) NOT NULL AUTO_INCREMENT,
      `username` varchar(20) DEFAULT NULL,
      `password` varchar(100) DEFAULT NULL,
      UNIQUE KEY `id` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    
    insert  into `users`(`id`,`username`,`password`) values (1,'fuwh','123456');

    我们需要定义一个shiro_jdbc.ini文件如下

    [main]
    dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
    dataSource.driverClass=com.mysql.jdbc.Driver
    dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro
    dataSource.user=root
    dataSource.password=rootadmin
    
    ;this is comment read from mysql
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    jdbcRealm.dataSource=$dataSource
    securityManager.realms=$jdbcRealm

    编写登陆认证代码:

    package com.fuwh.demo;
    
    import org.apache.shiro.SecurityUtils;
    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 ShiroDemo02 {
        
        private static Logger log=LoggerFactory.getLogger(ShiroDemo02.class);
        public static void main(String[] args) {
            //取得SecurityManager工厂
            Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc.ini");
            //取得SecurityManager实例
            SecurityManager securityManager=factory.getInstance();
            //将securityManager绑定到SecurityUtil
            SecurityUtils.setSecurityManager(securityManager);
    
            /*    至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了    */
    
            //取得当前用户
            Subject currentUser=SecurityUtils.getSubject();
            
            //使用shiro来进行登陆验证
            if(!currentUser.isAuthenticated()) {
                UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123456");
                try {
                    currentUser.login(token);
                    log.info("登陆成功!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("认证失败...");
                }
            }
            
            currentUser.logout();
        }
    }

    执行结果:

    2017-08-26 15:16:03,357 [main] INFO  [com.mchange.v2.log.MLog] - MLog clients using log4j logging.
    2017-08-26 15:16:04,107 [main] INFO  [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
    2017-08-26 15:16:04,436 [main] INFO  [org.apache.shiro.config.IniSecurityManagerFactory] - Realms have been explicitly set on the SecurityManager instance - auto-setting of realms will not occur.
    2017-08-26 15:16:04,560 [main] 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, dataSourceName -> 1hgetj59q83i1dm1es8ork|67f89fa3, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hgetj59q83i1dm1es8ork|67f89fa3, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/db_shiro, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
    2017-08-26 15:16:05,040 [main] INFO  [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
    2017-08-26 15:16:05,056 [main] INFO  [com.fuwh.demo.ShiroDemo02] - 登陆成功!!!

    此时,已经可以从表中去验证登陆了。那处理流程又是什么样的呢?

    首先我们使用shiro_jdbc.ini来初始化了SecurityManager,在配置文件中,我们定义了连接池信息,还有jdbcRealm,同时在SecurityManager中指定了realm为定义的JjdbcRealm,这时候,其实shiro使用的SecurityManager是一个RealmSecurityManager的实例。而当我们登陆的时候,则会通过配置的jdbcRealm来从数据库中取得用户信息来进行认证。那是怎么取得呢?我们明明没有写sql什么的。

    其实,看org.apache.shiro.realm.jdbc.JdbcRealm的源码可以看到,在这个类里面定义了很多的静态sql变量,点此查看点此查看sql内容

    其中比较重要的是AuthenticationQuery这个字段,默认情况下它的值是等于Default_Authentication_query。

    Default_Authentication_Query="select password from users where username=?";

    所以在默认情况下,它会从users这个表中通过username这个key来查找password。从而拿来跟我们提交的密码来进行匹配验证。

    如果我们不想使用默认的数据库,默认的表名,默认的列名的话,也可以通过在配置文件中重写AuthenticationQuery的值来个性化sql文。

    首先修改新建一个表members:

    USE `db_shiro`;
    
    DROP TABLE IF EXISTS `members`;
    
    CREATE TABLE `members` (
      `id` INT(4) NOT NULL AUTO_INCREMENT,
      `userName` VARCHAR(20) DEFAULT NULL,
      `pass` VARCHAR(100) DEFAULT NULL,
      UNIQUE KEY `id` (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    
    
    INSERT  INTO `members`(`id`,`userName`,`pass`) VALUES (1,'fuwh','123');

    然后修改shiro_jdbc_sql.ini配置文件:

    [main]
    dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
    dataSource.driverClass=com.mysql.jdbc.Driver
    dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro
    dataSource.user=root
    dataSource.password=rootadmin
    
    ;this is comment read from mysql
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    jdbcRealm.dataSource=$dataSource
    jdbcRealm.authenticationQuery=select pass from members where userName=? 
    securityManager.realms=$jdbcRealm

    修改认证程序的配置文件:

    package com.fuwh.demo;
    
    import org.apache.shiro.SecurityUtils;
    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 ShiroDemoSql02 {
        
        private static Logger log=LoggerFactory.getLogger(ShiroDemoSql02.class);
        public static void main(String[] args) {
            //取得SecurityManager工厂
            Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc_sql.ini");
            //取得SecurityManager实例
            SecurityManager securityManager=factory.getInstance();
            //将securityManager绑定到SecurityUtil
            SecurityUtils.setSecurityManager(securityManager);
    
            /*    至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了    */
            
            //取得当前用户
            Subject currentUser=SecurityUtils.getSubject();
            
            //使用shiro来进行登陆验证
            if(!currentUser.isAuthenticated()) {
                UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123");
                try {
                    currentUser.login(token);
                    log.info("登陆成功!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("认证失败...");
                }
            }
            
            currentUser.logout();
        }
    }

    后面我们会讲到角色认证也是同样的道理。

    自定义Realm

    上面我们使用的是Shiro提供的默认的Realm,下面我们自定义一个从数据库中读取信息的Realm,通过继承AuthorizingRealm。

    package com.fuwh.realm;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    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.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    import com.fuwh.util.DbUtil;
    
    public class MyJdbcRealm extends AuthorizingRealm{
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            // TODO Auto-generated method stub
            Connection conn=DbUtil.getConnection();
            String sql="select * from members2 where username=?";
            try {
                PreparedStatement ps=conn.prepareStatement(sql);
                ps.setString(1, token.getPrincipal().toString());
                ResultSet rs=ps.executeQuery();
                while(rs.next()) {
                    AuthenticationInfo info=new SimpleAuthenticationInfo(rs.getString("username"),rs.getString("password"),"salt");
                    return info;
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            return null;
        }
        
    }

    修改配置文件

    [main]
    myJdbcRealm=com.fuwh.realm.MyJdbcRealm
    securityManager.realms=$myJdbcRealm

    编写登陆类:

    package com.fuwh.demo;
    
    import org.apache.shiro.SecurityUtils;
    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 ShiroDemoMySql02 {
        
        private static Logger log=LoggerFactory.getLogger(ShiroDemoMySql02.class);
        public static void main(String[] args) {
            //取得SecurityManager工厂
            Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc_my_sql.ini");
            //取得SecurityManager实例
            SecurityManager securityManager=factory.getInstance();
            //将securityManager绑定到SecurityUtil
            SecurityUtils.setSecurityManager(securityManager);
    
            /*    至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了    */
            
            //取得当前用户
            Subject currentUser=SecurityUtils.getSubject();
            
            //使用shiro来进行登陆验证
            if(!currentUser.isAuthenticated()) {
                UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123");
                try {
                    currentUser.login(token);
                    log.info("登陆成功!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("认证失败...");
                }
            }
            
            currentUser.logout();
        }
    }

     那如果我们定义了多个Realm呢?

    在编写一个Realm类:

    package com.fuwh.realm;
    
    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.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    public class MyJdbcRealm2 extends AuthorizingRealm{
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            // TODO Auto-generated method stub
            return    new SimpleAuthenticationInfo(token.getPrincipal(),"1234","salt");
        } 
    }

    修改shiro_jdbc_my_sql_2.ini配置文件:

    [main]
    myJdbcRealm=com.fuwh.realm.MyJdbcRealm
    myJdbcRealm2=com.fuwh.realm.MyJdbcRealm2
    authStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
    securityManager.realms=$myJdbcRealm,$myJdbcRealm2
    securityManager.authenticator.authenticationStrategy=$authStrategy

    修改登陆类:

    package com.fuwh.demo;
    
    import org.apache.shiro.SecurityUtils;
    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 ShiroDemoMySql02_2 {
        
        private static Logger log=LoggerFactory.getLogger(ShiroDemoMySql02_2.class);
        public static void main(String[] args) {
            //取得SecurityManager工厂
            Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro_jdbc_my_sql_2.ini");
            //取得SecurityManager实例
            SecurityManager securityManager=factory.getInstance();
            //将securityManager绑定到SecurityUtil
            SecurityUtils.setSecurityManager(securityManager);
    
            /*    至此为止,简单的从mysql数据库读取realm信息的shiro环境就配置好了    */
            
            //取得当前用户
            Subject currentUser=SecurityUtils.getSubject();
            
            //使用shiro来进行登陆验证
            if(!currentUser.isAuthenticated()) {
                UsernamePasswordToken token=new UsernamePasswordToken("fuwh","123");
                try {
                    currentUser.login(token);
                    log.info("登陆成功!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("认证失败...");
                }
            }
            
            currentUser.logout();
        }
    }

    上面的例子中使用的认证策略是只有一个成功就认为是成功。

    在Shiro中还提供了下面几个策略:

    FirstSuccessfulStrategy:只有第一个认证成功的信息会被用

    AllSuccessfulStrategy:只有当所有的都成功的时候才认为是认证成功。

    源码地址:https://github.com/oukafu/shiro

  • 相关阅读:
    SuperSocket中的Server是如何初Start的
    SuperSocket中的Server是如何初Initialize的
    Explicit Interface Implementation (C# Programming Guide)
    Interfaces (C# Programming Guide)
    Java泛型Restletclient
    jQuery 完成ajax传jsonObject数据,并在后台处理
    SDUT 2933-人活着系列Streetlights(最小生成树Kruskal+和理查德设置来实现)
    华为OJ:查找字符的第一个字符串只出现一次
    Linux查找多个类似,但不同的名称和重命名文件
    【Java收集的源代码分析】Hashtable源代码分析
  • 原文地址:https://www.cnblogs.com/zerotomax/p/7420100.html
Copyright © 2011-2022 走看看