Shiro之身份驗證

jopen 10年前發布 | 144K 次閱讀 Shiro 安全相關

principals即主體的標識屬性,可以是任何東西,如用戶名、郵箱等,唯一即可。credentials是證明/憑證,即只有主體知道的安全值,如密碼/數字證書等。最常見的principals和credentials組合就是用戶/密碼了。

下面我們來看一個認證的例子,由于我們是用maven構建的實例,所以需要在pom.xml中添加依賴:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11-20120805-1225</version>
</dependency>
    <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.3</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.3</version>
</dependency>

另外,準備一些用戶微分憑據,shiro.ini:

[users]
zhang=123
wang=123

測試用例:

package org.shiro;

import junit.framework.Assert;
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.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

public class ShiroTest1{
    @Test
    public void shiro_test1(){
        //獲取SecurityManager工廠,此處使用ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
                new IniSecurityManagerFactory("classpath:shiro.ini");
        //得到SecurityManager實例并綁定給SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang","123");
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            System.err.println(e.getMessage());
        }
        //斷言用戶已經登錄
        Assert.assertEquals(true, subject.isAuthenticated());
        //退出
        subject.logout();
    }
}

從上例中,我們可以看到shiro的身份認證流程,如果還沒有明白,可以看看下圖:

  1. 首先調用Subject.login(token)進行登錄,其會自動委托給SecurityManager,調用之前必須通過SecurityUtils.setSecurityManager()設置

2.    SecurityManager負責真正的身份驗證邏輯,它會委托給Authenticator進行身份驗證

3.    Authenticator才是真正的身份驗證者,shiro API中核心的身份認證入口點,此處可以自定義插入自己的實現

4.    Authenticator可能會委托給相應的AuthenticationStrategy進行多Realm身份驗證,默認ModularRealmAuthenticator會調用AuthenticationStrategy進行多Realm身份驗證。

5.    Authenticator會把相應的token傳入Realm,從Realm獲取身份驗證信息,如果沒有返回/拋出異常表示身份驗證失敗了。此外可以配置多個Realm,將按照相應的順序及策略進行訪問。

 

Realm

Shiro從Realm獲取安全數據(如用戶、角色、權限),就是說SecurityManager要驗證用戶身份,那么它需要從Realm獲取相應的用戶進行比較以確定用戶身份是否合法。也需要從Realm得到用戶相應的角色/權限進行驗證用戶是否能進行操作。可以把Realm看成DataSource,即安全數據源。如我們前面的例子使用ini配置,它使用的是org.apache.shiro.realm.text.IniRealm。

org.apache.shiro.realm.Realm接口如下:

public interface Realm {
    String getName();                                //返回一個唯一的Realm名字
    boolean supports(AuthenticationToken token);     //判斷此Realm是否支持此Token
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException;          //根據Token獲取認證信息
}

單Realm配置實例如下:

MyRealm.java:

package org.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class MyRealm implements Realm {

    public String getName() {
        return "myRealm";
    }

    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        System.out.println("myrealm~~~~~~~~~~~~~~~~~~~~~~~~~~");
        //得到用戶名  
        String username = (String)token.getPrincipal(); 
        //得到密碼  
        String password = new String((char[])token.getCredentials()); 
        if(!"zhang".equals(username)) {  
            //如果用戶名錯誤  
            throw new UnknownAccountException(); 
        }  
        if(!"123".equals(password)) {  
            //如果密碼錯誤  
            throw new IncorrectCredentialsException(); 
        }  
        //如果身份認證驗證成功,返回一個AuthenticationInfo實現;  
        return new SimpleAuthenticationInfo(username, password, getName());  
    }
}

shiro-realm.ini:

myRealm=org.shiro.MyRealm
securityManager.realms=$myRealm

ShiroTest2.java:

package org.shiro;

import junit.framework.Assert;
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.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

public class ShiroTest2{

    @Test
    public void shiro_test1(){
        //獲取SecurityManager工廠,此處使用ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
                new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        //得到SecurityManager實例并綁定給SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang","123");
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            System.err.println(e.getMessage());
        }
        //斷言用戶已經登錄
        Assert.assertEquals(true, subject.isAuthenticated());
        //退出
        subject.logout();
    }
}

多Realm配置實例如下:

MyRealm1.java

package org.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class MyRealm1 implements Realm {

    public String getName() {
        return "myRealm1";
    }

    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        System.out.println("myRealm1--------------------");
        //得到用戶名  
        String username = (String)token.getPrincipal(); 
        //得到密碼  
        String password = new String((char[])token.getCredentials()); 
        if(!"zhang".equals(username)) {  
            //如果用戶名錯誤  
            throw new UnknownAccountException(); 
        }  
        if(!"123".equals(password)) {  
            //如果密碼錯誤  
            throw new IncorrectCredentialsException(); 
        }  
        //如果身份認證驗證成功,返回一個AuthenticationInfo實現;  
        return new SimpleAuthenticationInfo(username, password, getName());  
    }
}

MyRealm2.java:

package org.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class MyRealm2 implements Realm {

    public String getName() {
        return "myRealm2";
    }

    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }

    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        System.out.println("myRealm2--------------------");
        //得到用戶名  
        String username = (String)token.getPrincipal(); 
        //得到密碼  
        String password = new String((char[])token.getCredentials()); 
        if(!"zhang".equals(username)) {  
            //如果用戶名錯誤  
            throw new UnknownAccountException(); 
        }  
        if(!"123".equals(password)) {  
            //如果密碼錯誤  
            throw new IncorrectCredentialsException(); 
        }  
        //如果身份認證驗證成功,返回一個AuthenticationInfo實現;  
        return new SimpleAuthenticationInfo(username, password, getName());  
    }
}

shiro-multi-realm.ini:

myRealm1=org.shiro.MyRealm1
myRealm2=org.shiro.MyRealm2
securityManager.realms=$myRealm1,$myRealm2

ShiroTest3.java

package org.shiro;

import junit.framework.Assert;
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.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

public class ShiroTest3{

    @Test
    public void shiro_test1(){
        //獲取SecurityManager工廠,此處使用ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
                new IniSecurityManagerFactory("classpath:shiro-multi-realm.ini");
        //得到SecurityManager實例并綁定給SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang","123");
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            System.err.println(e.getMessage());
        }
        //斷言用戶已經登錄
        Assert.assertEquals(true, subject.isAuthenticated());
        //退出
        subject.logout();
    }
}

注意ini配置中,都是通過$name來引用自定義的Realm的。另外,securityManager會按照realms指定的順序進行身份認證。如果刪除了“securityManager.realms=$myRealm1,$myRealm2”,那么securityManager會按照realm聲明的順序進行使用(即無需設置realms屬性,其會自動發現)。當我們顯示指定realm后,其他沒有指定realm將被忽略。

 

shiro默認提供的Realm

以后一般繼承AuthorizingRealm即可,其繼承了AuthenticatingRealm(即身份驗證),而且也間接繼承了CachingRealm(帶有緩存實現)。

 

JDBC Realm使用

示例,我們先在pom.xml中添加兩年操作數據庫的依賴

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>apl</groupId>
    <artifactId>shiro-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11-20120805-1225</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>0.2.23</version>
        </dependency>
    </dependencies>
</project>

shiro-jdbc-realm.ini

dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=root
jdbcRealm.dataSource=$dataSource
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
securityManager.realms=$jdbcRealm

ShiroTest4.java測試用例:

public class ShiroTest4 {
    @Test
    public void testJDBCRealm() {
        //1、獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory =
                new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini");

        //2、得到SecurityManager實例 并綁定給SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        //3、得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");

        try {
            //4、登錄,即身份驗證
            subject.login(token);
        } catch (AuthenticationException e) {
            //5、身份驗證失敗
            e.printStackTrace();
        }

        Assert.assertEquals(true, subject.isAuthenticated()); //斷言用戶已經登錄

        //6、退出
        subject.logout();
    }

}

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!