shiro基础之认证授权

一、从Helloworld探究原理

public static void main(String[] args) {

    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();

    SecurityUtils.setSecurityManager(securityManager);

    // 获取当前的 Subject. 调用 SecurityUtils.getSubject();
    Subject currentUser = SecurityUtils.getSubject();

    // Do some stuff with a Session (no need for a web or EJB container!!!)
    // 测试使用 Session 
    // 获取 Session: Subject#getSession()
    Session session = currentUser.getSession();
    session.setAttribute("someKey", "aValue");
    String value = (String) session.getAttribute("someKey");
    if (value.equals("aValue")) {
        log.info("---> Retrieved the correct value! [" + value + "]");
    }

    // let's login the current user so we can check against roles and permissions:
    // 测试当前的用户是否已经被认证. 即是否已经登录. 
    // 调动 Subject 的 isAuthenticated() 
    if (!currentUser.isAuthenticated()) {
        // 把用户名和密码封装为 UsernamePasswordToken 对象
        UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
        // rememberme
        token.setRememberMe(true);
        try {
            // 执行登录. 
            currentUser.login(token);
        } 
        // 若没有指定的账户, 则 shiro 将会抛出 UnknownAccountException 异常. 
        catch (UnknownAccountException uae) {
            log.info("----> There is no user with username of " + token.getPrincipal());
            return; 
        } 
        // 若账户存在, 但密码不匹配, 则 shiro 会抛出 IncorrectCredentialsException 异常。 
        catch (IncorrectCredentialsException ice) {
            log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");
            return; 
        } 
        // 用户被锁定的异常 LockedAccountException
        catch (LockedAccountException lae) {
            log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                    "Please contact your administrator to unlock it.");
        }
        // ... catch more exceptions here (maybe custom ones specific to your application?
        // 所有认证时异常的父类. 
        catch (AuthenticationException ae) {
            //unexpected condition?  error?
        }
    }

    //认证之后授权
    //test a role:
    // 测试是否有某一个角色. 调用 Subject 的 hasRole 方法. 
    if (currentUser.hasRole("schwartz")) {
        log.info("----> May the Schwartz be with you!");
    } else {
        log.info("----> Hello, mere mortal.");
        return; 
    }

    //a (very powerful) Instance Level permission:
    // 测试用户是否具备某一个行为. 
    if (currentUser.isPermitted("user:delete:zhangsan")) {
        log.info("----> You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                "Here are the keys - have fun!");
    } else {
        log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
    }
}

二、执行流程

1、通过ini配置文件创建securityManager
helloWord使用的读取ini(含有用户、角色和权限配置)文件的形式,使用的是IniRealm ,正式使用时ini文件中的信息来自于通过自定义realm(继承AuthorizingRealm即可)查询的数据库

2、调用subject.login方法主体提交认证,提交的token
3、securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。

Authenticator 可能会委托给相应的 AuthenticationStrategy
行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用
AuthenticationStrategy 进行多 Realm 身份验证

4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
5、IniRealm根据输入的token(UsernamePasswordToken)从 shiro-first.ini查询用户信息,根据账号查询用户信息(账号和密码)
如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)AuthenticationInfo
正式使用时:在自定义的realm重写父类的doGetAuthenticationInfo的方法中,构建用户信息

//自定义relam
public class ShiroRealm extends AuthorizingRealm {

@Override
protected AuthenticationInfo doGetAuthenticationInfo(
        AuthenticationToken token) throws AuthenticationException {
    System.out.println("[FirstRealm] doGetAuthenticationInfo");
    
    //1. 把 AuthenticationToken 转换为 UsernamePasswordToken 
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    
    //2. 从 UsernamePasswordToken 中来获取 username
    String username = upToken.getUsername();
    
    //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
    System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");
    
    //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
    if("unknown".equals(username)){
        throw new UnknownAccountException("用户不存在!");
    }
    
    //5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常. 
    if("monster".equals(username)){
        throw new LockedAccountException("用户被锁定");
    }
    
    //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
    //以下信息是从数据库中获取的.
    //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象. 
    Object principal = username;
    //2). credentials: 密码. 
    Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
    if("admin".equals(username)){
        credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
    }else if("user".equals(username)){
        credentials = "098d2c478e9c11555ce2823231e02ec1";
    }
    
    //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
    String realmName = getName();
    //4). 盐值. 
    ByteSource credentialsSalt = ByteSource.Util.bytes(username);
    
    SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
    info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
    return info;
}
}

如果查询不到,就给ModularRealmAuthenticator返回null
6、ModularRealmAuthenticator接收IniRealm返回Authentication认证信息
如果返回的认证信息是null,ModularRealmAuthenticator抛出异常 (org.apache.shiro.authc.UnknownAccountException)
如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)

7、内部密码比对示意图

备注:图片中自定义的realm获取用户信息AuthenticationInfo,调用的是父类的父类(AuthenticatingRealm)中的getAuthenticationInfo方法(原图不见了,懒得重新画,做个备注算了)

AuthenticationStrategy
• AuthenticationStrategy 接口的默认实现:
• FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第
一个 Realm 身份验证成功的认证信息,其他的忽略;
• AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和
FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信
息;
• AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有
Realm身份验证成功的认证信息,如果有一个失败就失败了。
• ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy
策略

三、授权执行流程

1、首先调用 Subject.isPermitted/hasRole 接口,其会委托给SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
2、Authorizer是真正的授权者,如果调用如isPermitted(“user:view”),其首先会通过PermissionResolver 把字符串转换成相应的 Permission 实例;
3、在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限信息AuthorizationInfo用于匹配传入的角色/权限;
4、Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted/hasRole 会返回true,否则返回false表示授权失败。
5、授权内部示意图

猜你喜欢

转载自www.cnblogs.com/laoyin666/p/10625135.html