Shiro之身份验证

来源:互联网 发布:样本册设计软件 编辑:程序博客网 时间:2024/05/29 16:28

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=123wang=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.MyRealmsecurityManager.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.MyRealm1myRealm2=org.shiro.MyRealm2securityManager.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.DruidDataSourcedataSource.driverClassName=com.mysql.jdbc.DriverdataSource.url=jdbc:mysql://localhost:3306/shirodataSource.username=rootdataSource.password=rootjdbcRealm.dataSource=$dataSourcejdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealmsecurityManager.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();    }}
0 0
原创粉丝点击