NewBrandSTONE / BLogs

存放自己的总结
0 stars 0 forks source link

Shiro认证思路分析 #16

Open NewBrandSTONE opened 5 years ago

NewBrandSTONE commented 5 years ago

Shiro 的认证过程

  1. 新建 shiro-demo 工程

  2. 创建 shiro-test module

  3. 在 shiro-test 中引入相关依赖

    • junit
    • shiro-core
    • 最终 shiro-test 的 pom.xml 文件如下
    <?xml version="1.0" encoding="UTF-8"?>
    <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">
       <parent>
           <artifactId>shiro-demo</artifactId>
           <groupId>com.ozyoung.shiro</groupId>
           <version>1.0-SNAPSHOT</version>
       </parent>
       <modelVersion>4.0.0</modelVersion>
    
       <artifactId>shiro-test</artifactId>
    
       <dependencies>
           <dependency>
               <groupId>org.apache.shiro</groupId>
               <artifactId>shiro-core</artifactId>
           </dependency>
    
           <dependency>
               <groupId>junit</groupId>
               <artifactId>junit</artifactId>
               <version>RELEASE</version>
           </dependency>
       </dependencies>
    </project>

核心思想

  1. 构建 SecurityManager 环境
  2. 主体提交认证请求
    • Subject 提交认证请求道 Security Manager ,Security Manager 调用 Authenticor 来进行认证,Authenticor 调用 Realm 获取数据库中的数据,将 Subject 提交的数据和 Realms 从数据库中拿到的数据进行比较。

踩下的坑

  1. 运行以下代码报错 No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
    • 代码如下:
package com.young.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AuthenticationTest {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser() {
        simpleAccountRealm.addAccount("mike", "123456");
    }

    @Test
    public void testAuthentication() {

        // 1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        defaultSecurityManager.setRealm(simpleAccountRealm);

        // 2.主体提交认证
        Subject subject = SecurityUtils.getSubject();

        boolean isAuthenticated = subject.isAuthenticated();
        logger.info("主体认证-{}", isAuthenticated);

    }
}
package com.young.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {

    private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser() {
        simpleAccountRealm.addAccount("mike", "123456");
    }

    @Test
    public void testAuthentication() {

        // 1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        // 2.设置SecurityManager
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        // 2.主体提交认证
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mike", "123456");

        subject.login(usernamePasswordToken);

        boolean isAuthenticated = subject.isAuthenticated();

        System.out.println("主体认证-" + isAuthenticated);

    }
}

认证过程分析

  1. 使用 Subject.login(UsernamePasswordToken)

    • 调用 login 方法
    • 传入 UsernamePasswordToken 值
  2. 在 Subject.login(token) 的内部是通过调用 SecurityManager.login(delegateSubject, token)

  3. DefaultSercurityManager.login 方法调用 AuthenticatingSecurityManager.authenticate(token) 方法来实现

  4. authenticate 方法中,使用 authenticator.authenticate(token) 方法实现认证。

  5. authenticator 中调用 ModularRealmAuthenticator.doAuthenticate 方法实现认证

    • 其中获取了当前可用的 Realms,用来读取用户数据库或者传入的用户数据。
NewBrandSTONE commented 5 years ago

认证步骤

  1. 通过 SecurityUtils.getSubject(),获取当前访问系统的 Subject。
  2. 通过 Subject.isAuthenticated(),判断当前用户是否已经被认证,即是否已经登录。
  3. 若没有被认证,则把用户名和密码封装为 UsernamePasswordToken 对象。
    • 创建一个表单页面,用来提交用户名和密码
    • 把请求提交到 SpringMVC 的 Handler 上。
    • 获取用户名和密码。
  4. 调用执行登录的方法,Subject.login(token)。
  5. 自定义 Realm 的方法,从数据库中获取对应的记录,返回给 Shiro。
    • 实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
    • 实现 doGetAuthenticationInfo(AuthenticationToken)方法。
  6. 由 Shiro 完成对密码的比对。