shiro是什么

shiro是apache的一个开源安全框架,相比spring的security显的更轻量。

核心组件:

  • SecurityManager,大哥,它负责安全认证与授权
  • Subject,主体,可以是用户也可以是第三方服务
  • Realm,当需要与安全数据交互时,shiro就会从realm中去查找相应数据

创建一个简单的shiro项目

添加依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

构建一个简单的用户密码认证

/**
 * com.qd
 *
 * @author liyawei
 * @date 2018/4/25
 */
public class ShiroTest {

    @Test
    public void testShiro(){

        //初始化一个SimpleAccountRealm并设置一个账号密码
        SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
        simpleAccountRealm.addAccount("xiaodou","123456");

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

        //构建主体Subject
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        //构建一个用户密码令牌模拟登陆
        UsernamePasswordToken token = new UsernamePasswordToken("xiaodou","123456");
        subject.login(token);

        System.out.println("isAuthenticated:"+subject.isAuthenticated());
    }
}

以上整个过程是:Subject带着用户名和密码去找SecurityManager申请认证,SecurityManager会去Realm中查找安全数据并返回认证信息

这里我们账号密码是正确的,所以认证成功了。如果账号或密码错误会认证失败

使用ini配置进行验证

在resources里建一个ini配置文件,设置用户密码信息和角色权限信息

# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
[users]
xiaodou=123456,admin
xiaobai=666666,guest

# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
[roles]
admin=*
guest=user:look

从ini文件中获取SecurityManage并认证角色和权限,和上面那个差不多

/**
 * com.qd
 *
 * @author liyawei
 * @date 2018/4/25
 */
public class ShiroIniTest {

    @Test
    public void test(){

        // 从ini文件创建一个iniSecurityManager工厂,并获取一个SecurityManage实例
        IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:user.ini");
        SecurityManager securityManager = iniSecurityManagerFactory.getInstance();

        // 设置SecurityManager到静态内存区,单例模式,并获取一个主体Subject
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();

        // 构建一个用户密码进行登录
        UsernamePasswordToken token = new UsernamePasswordToken("xiaobai", "666666");
        subject.login(token);

        // 查看用户是否认证成功
        System.out.println("isAuthenticated:"+subject.isAuthenticated());

        // 查看用户是否有指定的角色
        System.out.println("hasRole:"+subject.hasRole("admin"));

        // 查看用户是否有指定的权限
        System.out.println("isPermission:"+subject.isPermitted("user:delete"));
    }
    
}

这里用户xiaobai并没有admin的角色和user:delete的权限所以下面两个是false

当然角色权限信息我们可以不用打印出来,实际开发中可能会try-catch捕捉到异常信息,根据不同的错误做不同的处理

自定义Realm

新建一个CustomRealm类继承AuthorizingRealm并实现doGetAuthorizationInfo和doGetAuthenticationInfo两个抽象方法

/**
 * com.qd
 *
 * @author liyawei
 * @date 2018/4/25
 */
public class CustomRealm extends AuthorizingRealm {

    private UserService userService = new UserServiceImpl();

    // 授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        User user = (User) principalCollection.getPrimaryPrincipal();

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 根据用户名从数据库中查询角色信息,返回值自己根据需求来写
        List<Permission> permissionList = permissionService.getByUsername(username);

        // 菜单+按钮权限
        for (Permission permission : permissionList) {
            if (StringUtil.isNotEmpty(permission.getCode())){
                info.addStringPermission(permission.getCode());
            }
        }

        return info;
    }

    // 认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 从token中获取用户名
        String username = (String) token.getPrincipal();

        // 从数据库中获取用户
        User user = userService.getByUsername(username);
        if (user == null) {
            return null;
        }

        // 根据用户名从数据库中查询角色信息,返回值自己根据需求来写
        Map<String, List<Permission>> permissionMap = permissionService.getByPermissionByUserId(user.getId());

        // 获取菜单权限和按钮权限
        user.setMenuList(permissionMap.get("menuList"));
        user.setPermissionList(permissionMap.get("permissionList"));

        // 返回认证信息
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), getName());

        return simpleAuthenticationInfo;
    }
}

认证时跟前面两种方式一样,只是defaultSecurityManager.setRealm(CustomRealm);这里改一下就行,这里就不多写垃圾代码了