tangbinde 发表于 2018-11-19 12:44:07

Apache Shiro学习笔记(一)Shiro简介

鲁春利的工作笔记,好记性不如烂笔头
  

  官网地址:http://shiro.apache.org/
  


  

  主要功能包括:
  Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情;常见的如:验证某个用户是否拥有某个角色。
  Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Integration:Web 集成,可以非常容易的集成到Web 环境;
  

  基本概念:
  Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject 都绑定到SecurityManager。

  SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且它管理着所有Subject;可以看出它是Shiro 的核心,它负责与后边介绍的其他组件进行交互。

  Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;可以把Realm看成DataSource,即安全数据源。

  

  Shiro中的Hello World!:
  配置文件(src/test/resources/shiro/first-shiro.ini):


lucl=123
wang=123  代码实现类:

/**
*
* @author lucl
*
* Authentication with shiro
*
*/
public class TestLoginWithShiro {
    private static final Logger logger = Logger.getLogger(TestLoginWithShiro.class);
    /**
   * test login
   */
    @Test
    public void testLoginSuccess () {
      // 1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
      Factory factory = new IniSecurityManagerFactory("classpath:shiro/first-shiro.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("lucl", "123");
      try{
            // 4、登录,即身份验证
            subject.login(token);
      } catch (AuthenticationException e) {
            // 5、身份验证失败
            logger.info("用户身份验证失败");
            e.printStackTrace();
      }
      Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录
      if (subject.isAuthenticated()) {
            logger.info("用户登录成功。");
      } else {
            logger.info("用户登录失败。");
      }
      // 6、退出
      subject.logout();
    }
}  

  身份认证流程:
  1、通过new IniSecurityManagerFactory 并指定一个ini 配置文件来创建一个SecurityManager工厂;
2、获取SecurityManager并绑定到SecurityUtils,这是一个全局设置,设置一次即可;
3、通过SecurityUtils得到Subject,其会自动绑定到当前线程;
4、调用subject.login 方法进行登录,其会自动委托给SecurityManager.login方法进行登录;
DelegatingSubject类的login方法:
public void login(AuthenticationToken token) throws AuthenticationException {
    clearRunAsIdentitiesInternal();
    Subject subject = securityManager.login(this, token);
    PrincipalCollection principals;
    String host = null;
    if (subject instanceof DelegatingSubject) {
      DelegatingSubject delegating = (DelegatingSubject) subject;
      //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
      principals = delegating.principals;
      host = delegating.host;
    } else {
      principals = subject.getPrincipals();
    }
    if (principals == null || principals.isEmpty()) {
      String msg = "Principals returned from securityManager.login( token ) returned a null or " +
                "empty value.This value must be non null and populated with one or more elements.";
      throw new IllegalStateException(msg);
    }
    this.principals = principals;
    this.authenticated = true;
    if (token instanceof HostAuthenticationToken) {
      host = ((HostAuthenticationToken) token).getHost();
    }
    if (host != null) {
      this.host = host;
    }
    Session session = subject.getSession(false);
    if (session != null) {
      this.session = decorate(session);
    } else {
      this.session = null;
    }
}  DefaultSecurityManager执行真正的登录操作:
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
      info = authenticate(token);
    } catch (AuthenticationException ae) {
      try {
            onFailedLogin(token, ae, subject);
      } catch (Exception e) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an " +
                        "exception.Logging and propagating original AuthenticationException.", e);
            }
      }
      throw ae; //propagate
    }
    Subject loggedIn = createSubject(token, info, subject);
    onSuccessfulLogin(token, info, loggedIn);
    return loggedIn;
}  IniRealm获取身份验证信息(AuthenticatingRealm类定义):
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    if (info == null) {
      //otherwise not cached, perform the lookup:
      info = doGetAuthenticationInfo(token);
      log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
      if (token != null && info != null) {
            cacheAuthenticationInfoIfPossible(token, info);
      }
    } else {
      log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }
    if (info != null) {
      assertCredentialsMatch(token, info);
    } else {
      log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].Returning null.", token);
    }
    return info;
}  IniRealm获取身份验证信息(SimpleAccountRealm类定义):
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    SimpleAccount account = getUser(upToken.getUsername());
    if (account != null) {
      if (account.isLocked()) {
            throw new LockedAccountException("Account [" + account + "] is locked.");
      }
      if (account.isCredentialsExpired()) {
            String msg = "The credentials for account [" + account + "] are expired";
            throw new ExpiredCredentialsException(msg);
      }
    }
    return account;
}  AuthenticatingRealm执行身份认证:
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    CredentialsMatcher cm = getCredentialsMatcher();
    if (cm != null) {
      if (!cm.doCredentialsMatch(token, info)) {
            //not successful - throw an exception to indicate this:
            String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
            throw new IncorrectCredentialsException(msg);
      }
    } else {
      throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
                "credentials during authentication.If you do not wish for credentials to be examined, you " +
                "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
    }
}  5、验证通过则身份认证成功;否则抛出异常。
  6、调用subject.logout退出,其会自动委托给SecurityManager.logout方法退出。
  




页: [1]
查看完整版本: Apache Shiro学习笔记(一)Shiro简介