Shiro实现身份验证

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shitianhang123/article/details/84942159

Shiro实现身份验证

身份验证,即在应用中谁能证明他是他本人,一般提供如他们的身份ID、用户名、密码等来证明。

在Shiro中,用户需要提供principals(身份)和credentials(证明)给Shiro,从而应用能验证用户身份:

  • principals: 身份,即主体的标识属性,可以是任何东西,如用户名、邮箱,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名、密码等。
  • credentials: 证明、凭证,即只有主体知道的安全值,如密码、数字证书等。

入门案例(登录退出)

shiro.ini

使用ini配置文件来模拟数据库中的数据,实际是应该从数据库中读取安全数据。

1
2
[users]
tycoding=123

AuthenticationTest.java 测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class AuthenticationTest {

    @Test
    public void loginLogoutTest(){
        //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
        Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");

        //2、得到SecurityManager实例,并绑定给SecurityUtils
        SecurityManager securityManager = (SecurityManager) factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        //3、得到Subject及创建用户名、密码身份的Token
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("tycoding","123");

        try{
            //4、登录,即身份验证
            subject.login(token);
        }catch (AuthenticationException e){
            //5、身份验证失败
            e.printStackTrace();
        }

        //判断用户是否已登录
        //用户登录成功将返回true,否则返回false并抛出异常
        System.out.println(subject.isAuthenticated());

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

解释:

  1. 首先需要创建SecurityManager工厂,这里使用ini配置文件来初始化SecurityManager工厂,以后使用Spring时,可通过将SecurityManager注入到Spring容器中,就不再用加载ini配置文件的方式。
  2. 通过工厂类获取到SecurityManager实例并绑定到SecurityUtils,这是一个全局设置,设置一次即可。
  3. 通过SecurityUtils得到Subject,其会自动绑定到当前线程;然后获取身份验证的Token,如用户名、密码等。
  4. 调用subject.login(token)方法登录,其会自动委托给SecurityManager.login()方法进行登录。
  5. 如果登录失败请捕获AuthenticationException或其子类,常见的异常有:DisabledAccountException(禁用的账号)、LockedAccountException(锁定的账号)、UnknownAccountException(错误的账号)、ExcessiveAttemptsException(登录失败次数过多)等。
  6. 最后调用subject.logout()退出,其会自动委托给SecurityManager.logout()退出。

身份认证流程

解释:

  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

Realm: 域,Shiro从Realm中获取安全数据(如用户、角色、权限);SecurityManager要验证用户身份,就需要从Realm中获取相应的用户进行比较以确定用户身份是否合法,

org.apache.shiro.realm.Realm接口中有如下方法:

1
2
3
4
5
6
String getName(); //返回一个唯一的Realm名字

boolean supports(AuthenticationToken token); //判断此Realm是否支持此token

//根据Token获取认证信息
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

自定义Realm实现

MyRealm.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MyRealm extends AuthorizingRealm {

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

    //用于授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    //用于身份验证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        System.out.println("进入自定义realm...");

        //得到用户名
        Object principal = token.getPrincipal();

        //得到密码,这个密码应该是从数据库中读取到的,而不是我们主动调用getCredentials()方法得到密码,这里仅为了测试

        //得到密码
        Object credentials = token.getCredentials();
//        Object credentials = "12";

        try{
            //如果身份验证失败就返回null,并抛出异常

            //如果身份验证成功就返回正确的信息(AuthenticationInfo的实现类)
            return new SimpleAuthenticationInfo(principal,credentials,getName());

        }catch (AuthenticationException e){
            e.printStackTrace();
            return null;
        }
    }
}

注释:

为了更清晰的展示自定义Realm参与了身份验证,特意打印了一行sys

其中密码应该是从数据库中读取的,这里从我们模拟的ini配置文件中读取安全信息(比如调用了service层获取的密码)。而token.getCredentials()是获取用户输入的密码。

注意:

注意最后的返回值类型是SimpleAuthenticationInfo(principal,credentials,realmName),它是AuthenticationInfo接口的一个实现类。它会返回校验结果:

  • 校验成功:返回包含安全数据的SimpleAuthenticationInfo实例对象。
  • 校验失败:返回null。

在实际中,我们应该从数据库中读取用户安全信息,我们也并不需要手动调用getCredentials()方法,因为在返回SimpleAuthenticationInfo时,Shiro会自动调用getCredentials()方法获取用户输入的密码,并与在数据库中读取到的密码进行比较,并将结果返回。

shiro-realm.ini

1
2
3
4
5
6
7
8
[main]
#声明一个realm
myRealm=com.shiro.MyRealm
#指定securityManager的Realm实现,使用$name来引入之前的realm定义
securityManager.realms=$myRealm

[users]
tycoding=123

注释:

这里我们使用ini来模拟数据库数据,并在其中指定自动realm实现,在使用Spring后,我们会将自定义realm实现注入到Spring配置文件中。

多Realm配置

1
2
3
4
5
6
7
8
9
10
[main]
#声明一个realm
myRealm=com.shiro.MyRealm
myRealm2=com.shiro.MyRealm2

#指定securityManager的Realm实现,使用$name来引入之前的realm定义
securityManager.realms=$myRealm,$myRealm2

[users]
tycoding=123

猜你喜欢

转载自blog.csdn.net/shitianhang123/article/details/84942159