Shiro基础教程

什么Shiro?

  1. Apache的强大灵活的开源安全框架
  2. 提供, 认证, 授权, 企业会话管理, 安全加密, 缓存管理

一般我们使用Shiro能够快捷方便的完成项目里的权限管理模块开发

Shiro与Spring Security

Apache ShiroSpring Security
简单,灵活,轻量级复杂,笨重,重量级
可脱离Spring不可脱离Spring
粒度较粗粒度更细

我个人更喜欢用Shiro, 主要因为它比较简单, 灵活, 有些没有的功能我们可以自己去拓展. 权限粒度较粗这一块, 因为我们基本上是基于资源去做权限控制. 如果我们要做数据权限的话, 一定会和我们业务代码耦合, 所以Spring Security的控制粒度更细也没有体现出来, 或者说没有体现的很明显. 而且Spring的官网它也是用的Shiro做安全管理.

Shiro整体架构

在这里插入图片描述

  • 首页最上面浅黄的部分可以理解为当前的----操作用户

Security Manager部分是我们Shiro的核心. 我们来看一下它的组件

  • Authenticator(认证器)管理我们的登陆, 登出
  • Authorizer(授权器) 赋予我们主体的权限
  • SessionManager(会话管理器)可以不在不借助任何web容器下使用Session
  • SessionDAO(会话操作)提供了Session的操作, 主要是有增删改查
  • CacheManager(缓存管理器)利用缓存管理器可以缓存我们的角色数据和权限数据
  • PluggableRealms(插接式连接器)Shiro获取认证信息, 权限数据, 角色数据, 连接数据库
  • Cryptography(加密器)可以使用它, 非常快捷便利的行进加密

用户提交请求到SecurityManager, SecurityManager调用认证器去做一个认证, 而认证器是通过插接式连接器, 从数据库中查询获取到认证信息. 授权器同样如此.

Shiro认证

  1. 创建SecurityManager对象, 构建SecurityManager的环境
  2. 主体提交认证(也就是上图中的最上面部分,操作用户)到SecurityManager进行认证
  3. SecurityManager调用Authenticator(认证器)进行认证, 进行认证的时候需要使用PluggableRealms(插接式连接器)获取认证数据, 然后再进行认证.

认证小demo(可以改一下不正确的角色认证试试看会有什么效果)

SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser(){
        //添加一个用户
        simpleAccountRealm.addAccount("ljj","123");
    }

    @Test
    public void testAuthentication(){
        //1. 构建SecurityManager环境
        DefaultSecurityManager manager = new DefaultSecurityManager();
        manager.setRealm(simpleAccountRealm);//设置Realm环境

        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(manager);//设置SecurityManager环境
        Subject subject = SecurityUtils.getSubject();//获得主体
        UsernamePasswordToken token = new UsernamePasswordToken("ljj","123");//存放验证数据
        subject.login(token);//进行验证

        //是否认证成功
        System.out.println(subject.isAuthenticated());

        //登出
        subject.logout();

        System.out.println(subject.isAuthenticated());
    }

Shiro授权

shiro授权和基本一致, 只不过是将Authenticator(认证器)更换成了Authorizer(授权器)

授权小demo (可以改一下不正确的角色权限试试看会有什么效果)

	@Before
    public void addUser(){
        //添加一个用户, 并赋予角色权限,可以有多个角色
        simpleAccountRealm.addAccount("ljj","123","admin","user");
    }

	@Test
    public void testAuthentication(){
        //1. 构建SecurityManager环境
        DefaultSecurityManager manager = new DefaultSecurityManager();
        manager.setRealm(simpleAccountRealm);//设置Realm环境

        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(manager);//设置SecurityManager环境
        Subject subject = SecurityUtils.getSubject();//获得主体
        UsernamePasswordToken token = new UsernamePasswordToken("ljj","123");//存放验证数据
        subject.login(token);//进行验证

        //是否认证成功
        System.out.println(subject.isAuthenticated());

        //权限认证, 单个是checkRole, 多个才要加 s
        subject.checkRoles("admin","user");
    }

Shiro—IniRealm

首页我们得在resources文件夹下创建一个*.ini文件
在这里插入图片描述
内容如下

[users]    //认证信息, 角色权限
ljj=123,admin
[roles]   //角色权限限制
admin=user:delete,user:update

java代码

@Test
    public void testAuthentication(){
		//创建IniRealm对象
        IniRealm iniRealm = new IniRealm("classpath:user.ini");

        //1. 构建SecurityManager环境
        DefaultSecurityManager manager = new DefaultSecurityManager();
        manager.setRealm(iniRealm);//设置Realm环境
        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(manager);//设置SecurityManager环境
        Subject subject = SecurityUtils.getSubject();//获得主体
        UsernamePasswordToken token = new UsernamePasswordToken("ljj","123");//存放验证数据
        subject.login(token);//进行验证

        //是否认证成功
        System.out.println(subject.isAuthenticated());

        subject.checkRole("admin");

        //判断是否具有对应操作的权限
        subject.checkPermission("user:update");
    }

大家可以尝试认证错误信息以及错误权限看看会发生什么情况

Shiro----jdbcRealm

感谢大家的关注, 关于连接数据库进行认证与授权的教程我将在Shiro实战教程中去编写, 点个赞支持一下作者.

Shiro—自定义

自定义Realm需要继承于AuthorizingRealm, 并且实现它的两个方法, 如下

	/**
     * 授权
     * @param principalCollection 授权信息
     * @return
     */
	@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

	/**
     * 认证
     * @param authenticationToken 认证信息
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }

如注释所描述一样, 在其方法中便可进行认证与授权了.

认证

public class CustomRealm extends AuthorizingRealm {

    Map<String, String> userMap = new HashMap<>();
    {
        userMap.put("ljj","123");
        //realmName可以随意取名
        super.setName("customRealm");
    }

    /**
     * 认证
     * @param authenticationToken 认证信息
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //1.从主体传过来的认证信息中,获取用户名
        String userName = (String) authenticationToken.getPrincipal();

        //2.通过用户名到数据库中获取凭证
        String password = getPasswordByUserName(userName);
        //如果不存在
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                "ljj",password,"customRealm");
        return authenticationInfo;
    }

    /**
     * 模拟数据库查询凭证
     * 一般我们需要在这里连接数据进行验证, 由于我这只是一个小demo就直接写了一个map
     * 大家可以去连接数据库进行验证
     * @param userName
     * @return
     */
    private String getPasswordByUserName(String userName){
        return userMap.get(userName);
    }

测试类

	@Test
    public void testAuthentication(){

        CustomRealm customRealm = new CustomRealm();

        //1. 构建SecurityManager环境
        DefaultSecurityManager manager = new DefaultSecurityManager();
        manager.setRealm(customRealm);//配置环境
        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(manager);//设置SecurityManager环境
        Subject subject = SecurityUtils.getSubject();//获得主体
        UsernamePasswordToken token = new UsernamePasswordToken("ljj","123");//存放验证数据
        subject.login(token);//进行验证

        //是否认证成功
        System.out.println(subject.isAuthenticated());

授权:

	/**
     * 授权
     *
     * @param principalCollection 授权信息
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        String userName = (String) principalCollection.getPrimaryPrincipal();
        //从数据库或者缓存中获取角色数据
        Set<String> roles = getRolesByUserName(userName);
        //从数据库或者缓存中获取权限数据
        Set<String> permissions = getPermissionsByUserName(userName);
        //创建SimpleAuthorizationInfo对象, 返回得到的数据
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //设置权限
        simpleAuthorizationInfo.setStringPermissions(permissions);
        //设置角色
        simpleAuthorizationInfo.setRoles(roles);
        return simpleAuthorizationInfo;
    }

    /**
     * 模拟数据库查询权限
     * @param userName
     * @return
     */
    private Set<String> getPermissionsByUserName(String userName) {
        Set<String> sets = new HashSet<>();
        sets.add("user:delete");
        sets.add("user:add");
        return sets;
    }

    /**
     * 模拟数据库查询授权
     * 这里和getPasswordByUserName一样
     *
     * @param userName
     * @return
     */
    private Set<String> getRolesByUserName(String userName) {
        Set<String> sets = new HashSet<>();
        sets.add("admin");
        sets.add("user");
        return sets;
    }

测试类
在认证测试类中加上这两句, 同样,大家可以尝试一下错误的认证与授权, 看看会发生什么情况

	//判断是否具有此角色信息
	subject.checkRole("admin");
     //判断是否具有对应操作的权限
    subject.checkPermissions("user:add","user:delete");

Shiro加密

Shiro散列配置

  • HashedCredentialsMatcher
  • 自定义Realm中使用散列
  • 盐的使用

首先我们先要创建一个HashedCredentialsMatcher工具类

	@Test
    public void testAuthentication(){

        CustomRealm customRealm = new CustomRealm();

        //1. 构建SecurityManager环境
        DefaultSecurityManager manager = new DefaultSecurityManager();
        manager.setRealm(customRealm);//配置环境

        //创建工具类
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //设置加密的名称,加密方式
        matcher.setHashAlgorithmName("md5");
        //设置加密的次数
        matcher.setHashIterations(1);
        //在自定义的Realm中配置HashedCredentialsMatcher对象
        customRealm.setCredentialsMatcher(matcher);


        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(manager);//设置SecurityManager环境
        Subject subject = SecurityUtils.getSubject();//获得主体
        UsernamePasswordToken token = new UsernamePasswordToken("ljj","123");//存放验证数据
        subject.login(token);//进行验证

        //是否认证成功
        System.out.println(subject.isAuthenticated());

//        subject.checkRole("admin");
//
//        //判断是否具有对应操作的权限
//        subject.checkPermissions("user:add","user:delete");
    }

在自定义类中创建main方法, 使用md5获得加密后的密码

		public static void main(String[] args) {
        //得到加密数据
        Md5Hash md5Hash = new Md5Hash("123");
        System.out.println(md5Hash.toString());
   	 }

更改map里的密码为加密后的数据(数据库里的密码都是经过加密的只不过我这里的map是用来模拟)

但是由于密码只进行了一次加密, 安全级别还是达不到我们所需要的级别, 所以我们需要进行加盐.
获得加盐数据

 public static void main(String[] args) {
        //得到加盐数据
        Md5Hash md5Hash = new Md5Hash("123","ljj");
        System.out.println(md5Hash.toString());
    }

更改map里面的密码
我们还需要在认证方法中添加这样一句代码
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(“ljj”)); 需要把所加的盐设置进SimpleAuthenticationInfo

	 /**
     * 认证
     *
     * @param authenticationToken 认证信息
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //1.从主体传过来的认证信息中,获取用户名
        String userName = (String) authenticationToken.getPrincipal();

        //2.通过用户名到数据库中获取凭证
        String password = getPasswordByUserName(userName);
        //如果不存在
        if (password == null) {
            return null;
        }
        //创建SimpleAuthenticationInfo,返回数据
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                "ljj", password, "customRealm");
        //需要把所加的盐设置进来               直接将字符串转换成ByteSource对象
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("ljj"));
        return authenticationInfo;
    }

在下一篇博客中, 我将带着大家从数据库和缓存中获取数据, 跟真实的项目完全一致, 谢谢大家的关注

springBoot整合Shiro实战教程

https://blog.csdn.net/I_No_dream/article/details/93227335

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页