shiro

5 篇文章 0 订阅
1 篇文章 0 订阅

shiro

1、主流安全框架

shiro:Apache开源,简单易用,集成没有限制 https://shiro.apache.org/,

shiro学习资料:https://www.w3cschool.cn/shiro/andc1if0.htmlhttps://www.bilibili.com/video/BV1uz4y197Zm?spm_id_from=333.999.0.0

Spring Security:Spring家族,只能集成在Spring家族相关框架中。

2、解决问题:权限管理

权限管理包括:认证和授权

认证:用户登录

授权:访问控制。进行身份认证后,不同身份的用户能访问的资源不同。

3、架构图

在这里插入图片描述

1)Subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证授权。

2)Security Manager:安全管理器,主体进行认证授权都是通过Security Manager进行的,Security Manager是一个包含了许多模块的容器。

3)Authentication:认证器,主体认证通过authentication进行。

4)Authorizer:授权器,主体授权通过authorizer进行。

5)Session Manager:web应用中一般是web容器(tomcat)对session进行管理,shiro中也提供session管理方案。

6)Session Dao:提供了session的crud操作。

7)Cache Manager:缓存控制器,来管理如用户、角色、权限的缓存,放到缓存中能提高访问性能。

8)Cryptography:提供了多种密码加密策略

9)Realms:域,相当于数据源,用于或获取安全实体的;可以是JDBC实现,也可以内存实现等。注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

4、shiro中的认证

shiro认证中的关键对象:

1)Subject:主体,访问系统的用户,主体可以是:用户、程序等,进行认证的都称为主体。

2)Principal:身份信息,是主体进行身份认证的标识,标识具有唯一性,一个主体可以有多个身份,但必须有一个主身份(Primary Principal)。

3)Credential:凭证信息,只有主体知道的安全信息,如密码、证书等。

4.1、身份认证流程

4.2、快速入门

1)导坐标

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

2)配置文件

在这里插入图片描述

预先将用户信息存入配置文件中(以后连接数据库),shiro配置文件比较特殊,后缀名为.ini

[users]
wang=123
zhangsan=1234
lisi=12345

3)编写代码

public class TestAuthenticator {
    public static void main(String[] args) {
        //1.创建安全管理器对象 Security Manager
        DefaultSecurityManager securityManager =new DefaultSecurityManager();

        //2.给安全管理器设置Realms, 读取准备好的用户信息
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

        //3.SecurityUtils 全局安全工具类 给全局的安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        //4.关键对象,subject主体,
        Subject subject = SecurityUtils.getSubject();

        //5.创建令牌token,authentication
        UsernamePasswordToken token = new UsernamePasswordToken("wang","123");

        try {
            System.out.println("认证之前状态:"+subject.isAuthenticated());
            subject.login(token);//用户认证
            System.out.println("认证之后状态:"+subject.isAuthenticated());
        }catch (UnknownAccountException e1){
            e1.printStackTrace();
            System.out.println("用户名不存在");
        }
        catch (IncorrectCredentialsException e2) {
            e2.printStackTrace();
            System.out.println("密码错误");
        }
    }
}
  • 成功案例
    在这里插入图片描述
  • 用户名错误
    在这里插入图片描述
  • 密码错误
    在这里插入图片描述

4)通过调试debug源码之后,发现在认证执行流程中:

  • 最终执行用户名比较的是:SimpleAccountRealm类中的doGetAuthenticationInfo()方法

  • 最终执行密码校验的是:AuthenticationRealm类中的assertCredentialsMatch()方法

5)总结
在这里插入图片描述

AuthenticatingRealm抽象类中认证realm的方法为doGetAuthenticationInfo()

AuthorizingRealm抽象类中授权realm的方法为doGetAuthorizationInfo()

SimpleAccountRealm实现类中有两个方法一个是认证一个是授权

4.3、自定义realm

在这里插入图片描述

自定义realm

/**
 * 实现自定义Realm,将认证/授权来源转为数据库实现,实现AuthorizingRealm抽象类,并实现方法
 */
public class CustomerRealm extends AuthorizingRealm {


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

    /**
     * 认证方法
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = token.getPrincipal().toString();
        //根据用户名(principal)从数据库中查询
        if ("wang".equals(principal)) {
            //参数1:用户名  参数2:从数据库中查询密码  参数3:提供当前的realm的名字 this.getName()
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, "123", this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

使用自定义realm进行认证

/**
 * 使用自定义Realm
 */
public class TestCustomerRealmAuthenticator {

    public static void main(String[] args) {
        //创建security manager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //设置自定义realm
        defaultSecurityManager.setRealm(new CustomerRealm());

        //给安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        //通过安全工具类获取主体 subject
        Subject subject = SecurityUtils.getSubject();

        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("wang", "1231");

        try {
            subject.login(token);
        } catch (UnknownAccountException e0) {
            e0.printStackTrace();
            System.out.println("用户名错误");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误");
        }
    }
}

4.4、自定义realm + md5 + salt + hash

public class TestMd5Salt {
    public static void main(String[] args) {
        Md5Hash md5Hash = new Md5Hash("123");
        System.out.println(md5Hash.toHex());


        Md5Hash md5Hash1 = new Md5Hash("123", "abc");
        System.out.println(md5Hash1.toHex());


        Md5Hash md5Hash2 = new Md5Hash("123", "abc", 1024);
        System.out.println(md5Hash2.toHex());
    }

}

在这里插入图片描述
1)自定义realm + md5 + salt + hash

/**
 * 使用自定义realm + md5 + salt + hash
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取身份信息
        String principal = token.getPrincipal().toString();
        //根据用户名查询数据库
        //模拟
        if ("wang".equals(principal)) {
            //参数1:用户名  参数2:数据库md5+salt之后的密码  参数3:随机盐  参数4:realm的名
            return new SimpleAuthenticationInfo(principal, "894b3913a4a13b25dc6186d11835c209",
                    ByteSource.Util.bytes("abc"), this.getName());
        }
        return null;
    }
}

2)使用自定义realm + md5 + salt + hash进行认证

public class TestCustomerMd5RealmAuthenticator {
    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //注入自定义realm
        CustomerMd5Realm realm = new CustomerMd5Realm();
        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //散列的次数
        hashedCredentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        defaultSecurityManager.setRealm(realm);//注入

        //将安全管理器注入安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类获取 主体subject
        Subject subject = SecurityUtils.getSubject();
        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("wang", "123");
        try {
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        } catch (IncorrectCredentialsException e1) {
            e1.printStackTrace();
            System.out.println("密码错误");
        }
    }
}

在这里插入图片描述

5、shiro授权

5.1、授权

授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。

5.2、关键对象

在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

  • Subject 主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。

  • Resource 资源,在应用中用户可以访问的URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

  • Permission 权限/许可,规定了主体对资源的操作许可,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源。

5.3、授权方式

  • 基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制
if(subject.hasRole("admin")){
	//操作什么资源
}
  • 基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问
if(subject.isPermission("user:update:01")){
	//对01用户进行修改
}

5.4、权限字符串

权限字符串的规则:资源标识符:操作:资源实例表示符表示为对哪个资源的哪个实例具有什么操作,”:“是资源/操作/实例的分隔符,权限字符串也可以使用 * 通配符。

5.5、shiro中授权的实现方式

  • 编程式
Subject subject=SecurityUtils.getSubject();
if(Subject.hasRole("admin")){
	//有权限
}else{
	//无权限
}
  • 注解式
@RequiresRoles("admin")
public void hello(){
	//有权限
}
  • 标签式
JSP标签
<shiro:hasRole name="admin">
<!--有权限-->
<shiro:hasRole>
Thymeleaf中使用shiro需要额外集成

5.6、授权实现

自定义realm

/**
 * 使用自定义realm + md5 + salt + hash
 */
public class CustomerMd5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取主身份信息
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        System.out.print("身份信息:" + primaryPrincipal+" ");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //将数据库中查询的角色信息赋值给权限对象, 模拟
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRoles(Arrays.asList("user", "test"));
        //将数据库中查询的权限信息赋值给权限对象,模拟
        simpleAuthorizationInfo.addStringPermission("user:*:*");
        return simpleAuthorizationInfo;

    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取身份信息
        String principal = token.getPrincipal().toString();
        //根据用户名查询数据库
        //模拟
        if ("wang".equals(principal)) {
            //参数1:用户名  参数2:数据库md5+salt之后的密码  参数3:随机盐  参数4:realm的名
            return new SimpleAuthenticationInfo(principal, "894b3913a4a13b25dc6186d11835c209",
                    ByteSource.Util.bytes("abc"), this.getName());
        }
        return null;
    }
}

测试认证授权

public class TestCustomerMd5RealmAuthenticator {
    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //注入自定义realm
        CustomerMd5Realm realm = new CustomerMd5Realm();
        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //散列的次数
        hashedCredentialsMatcher.setHashIterations(1024);
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        defaultSecurityManager.setRealm(realm);//注入

        //将安全管理器注入安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //通过安全工具类获取 主体subject
        Subject subject = SecurityUtils.getSubject();
        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("wang", "123");
        try {
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        } catch (IncorrectCredentialsException e1) {
            e1.printStackTrace();
            System.out.println("密码错误");
        }

        //授权
        if (subject.isAuthenticated()) {
            //基于角色的权限控制
            System.out.println(subject.hasRole("admin"));
            System.out.println("-----------");

            //是否具有其中一个角色
            System.out.println(Arrays.toString(subject.hasRoles(Arrays.asList("admin", "student"))));
            System.out.println("-----------");

            //基于多角色的权限控制,同时需要具有多个角色
            System.out.println(subject.hasAllRoles(Arrays.asList("user", "test")));
            System.out.println("-----------");
            System.out.println(subject.hasAllRoles(Arrays.asList("admin", "student")));

            System.out.println("-----------");
            //基于权限字符串的访问控制 资源标识符:操作:资源类型
            System.out.println(subject.isPermitted("user:*:01"));
            System.out.println(subject.isPermitted("user:update:02"));
            System.out.println(subject.isPermitted("goods:update:01"));
            System.out.println("-----------");

            //分别具有哪些权限
            System.out.println(Arrays.toString(subject.isPermitted("user:*;01", "user:insert:*", "goods:delete:*")));
            System.out.println("-----------");
            
            //同时具有哪些权限
            System.out.println(subject.isPermittedAll("user:*:12", "goods:*:*"));
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值