Shiro的身份授权

289 阅读3分钟

身份授权

【1】基本流程

image.png

1、首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager。

2、SecurityManager接着会委托给内部组件Authorizer;

3、Authorizer再将其请求委托给我们的Realm去做;Realm才是真正干活的;

4、Realm将用户请求的参数封装成权限对象。再从我们重写的doGetAuthorizationInfo方法中获取从数据库中查询到的权限集合。

5、Realm将用户传入的权限对象,与从数据库中查出来的权限对象,进行一一对比。如果用户传入的权限对象在从数据库中查出来的权限对象中,则返回true,否则返回false。

进行授权操作的前提:用户必须通过认证。

在真实的项目中,角色与权限都存放在数据库中。为了快速上手,我们先创建一个自定义DefinitionRealm,模拟它已经登录成功。直接返回一个登录验证凭证,告诉Shiro框架,我们从数据库中查询出来的密码是也是就是你输入的密码。所以,不管用户输入什么,本次登录验证都是通过的。

/**
     * @Description 认证接口
     * @param token 传递登录token
     * @return
     */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    //从AuthenticationToken中获得登录名称
    String loginName = (String) token.getPrincipal();
    SecurityService securityService = new SecurityServiceImpl();
    Map<String, String> map = securityService.findPasswordByLoginName(loginName);
    if (map.isEmpty()){
        throw new UnknownAccountException("账户不存在");
    }
    String salt = map.get("salt");
    String password = map.get("password");
    //传递账号和密码:参数1:用户认证凭证信息,参数2:明文密码,参数三:字节salt,参数4:当前DefinitionRealm名称
    return  new SimpleAuthenticationInfo(loginName,password, ByteSource.Util.bytes(salt),getName());
}

接下来就实现授权的核心方法了

在DefinitionRealm中找到下列方法:

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    return null;
}

此方法的传入的参数PrincipalCollection principals,是一个包装对象,它表示"用户认证凭证信息"。包装的是谁呢?没错,就是认证doGetAuthenticationInfo()方法的返回值的第一个参数loginName。你可以通过这个包装对象的getPrimaryPrincipal()方法拿到此值,然后再从数据库中拿到对应的角色和资源,构建SimpleAuthorizationInfo。

/**
     * @Description 授权方法
     */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //拿到用户认证凭证信息
    String loginName = (String) principals.getPrimaryPrincipal();
    //从数据库中查询对应的角色和资源
    SecurityService securityService = new SecurityServiceImpl();
    //从数据库查询用户角色
    List<String> roles = securityService.findRoleByloginName(loginName);
    //从数据库查询用户权限或者资源
    List<String> permissions = securityService.findPermissionByloginName(loginName);
    //构建资源校验
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    authorizationInfo.addRoles(roles);
    authorizationInfo.addStringPermissions(permissions);
    return authorizationInfo;
}

【2】基本使用

package com.axl.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Assert;
import org.junit.Test;

/**
 * @Description:shiro的第一个例子
 */
public class HelloShiro {


    @Test
    public void testPermissionRealm() {
        Subject subject = shiroLogin("axl", "123");
        //判断用户是否已经登录
        System.out.println("是否登录成功:" + subject.isAuthenticated());

        //---------检查当前用户的角色信息------------
        System.out.println("是否有管理员角色:"+subject.hasRole("admin"));
        //---------如果当前用户有此角色,无返回值。若没有此权限,则抛 UnauthorizedException------------
        try {
            subject.checkRole("coder");
            System.out.println("有coder角色");
        }catch (Exception e){
            System.out.println("没有coder角色");
        }

        //---------检查当前用户的权限信息------------
        System.out.println("是否有查看订单列表资源:"+subject.isPermitted("order:list"));
        //---------如果当前用户有此权限,无返回值。若没有此权限,则抛 UnauthorizedException------------
        try {
            subject.checkPermissions("order:add", "order:del");
            System.out.println("有添加和删除订单资源");
        }catch (Exception e){
            System.out.println("没有有添加和删除订单资源");
        }

    }


    /**
     * @Description 登录方法
     */
    private Subject shiroLogin(String loginName,String password) {
        //导入权限ini文件构建权限工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //工厂构建安全管理器
        SecurityManager securityManager = factory.getInstance();
        //使用SecurityUtils工具生效安全管理器
        SecurityUtils.setSecurityManager(securityManager);
        //使用SecurityUtils工具获得主体
        Subject subject = SecurityUtils.getSubject();
        //构建账号token
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginName, password);
        //登录操作
        subject.login(usernamePasswordToken);
        return subject;
    }
}

【3】小结

1、鉴权需要实现doGetAuthorizationInfo方法

2、鉴权使用门面subject中方法进行鉴权

  • 以check开头的会抛出异常
  • 以is和has开头会返回布尔值