Shiro初步使用

Shiro初步使用

​ Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序

​ 这里主要以shiro的入门实战来了解到使用shiro的流程

1、认证

1、使用ini文件的方式使用shiro

INI配置文件

INI配置文件是一种key/value的键值对配置,分为[main]、[users]、[roles]、[urls]四个部分,每一个部分中的key不可重复,#号代表注释,shiro.ini文件默认在/WEB-INF/ 或classpath下,shiro会自动查找,INI配置文件相当于一个静态数据库。因此无需进行数据库操作

1、创建一个普通maven项目,并导入shiro的相关依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.5.3</version>
</dependency>
2、在resource目录下创建shiro.ini文件,第一行不可以写错
[users]
zhangsan=123
lisi=123456
3、编写代码

主要步骤有六步:

1.创建安全管理器对象
DefaultSecurityManager securityManager=new DefaultSecurityManager();

2.给安全管理器设置realm(这里使用ini文件)
securityManager.setRealm(new IniRealm(“classpath:shiro.ini”));

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

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

5.创建令牌
UsernamePasswordToken token=new UsernamePasswordToken(“zhangsan”,“123”);

6.使用token用户认证

subject.login(token);

认证的时候如果用户名不存在会报UnknownAccountException

认证的时候如果密码错误会报IncorrectCredentialsException

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

        //2.给安全管理器设置realm
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

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

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

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

        //6.使用token用户认证
        try {
            System.out.println("认证状态:"+ subject.isAuthenticated());
            subject.login(token);
            System.out.println("认证状态:"+ subject.isAuthenticated());
        }catch (UnknownAccountException e){
            System.out.println("用户名不存在");
            e.printStackTrace();
        }
        catch (IncorrectCredentialsException e) {
            System.out.println("密码错误");
            e.printStackTrace();
        }
    }
}

2、创建和使用自定义Realm

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MULqIUnv-1600505832253)(C:%5CUsers%5CAdmin%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20200918204444823.png)]

由类的关系图所示,ini方式使用的IniRealm类是继承了AuthorizingRealm抽象类,并且事实上自定义的Reaml就是需要继承AuthorizingRealm抽象类并且重写doGetAuthorizationInfo(授权)doGetAuthenticationInfo(认证),除了该处,其他与ini方式基本一致

添加链接描述

1、创建自定义Realm(这里使用的虚拟查库)
//创建自定义Realm
public class CustomerRealm extends AuthorizingRealm {

    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //根据该token拿到用户名
        String principal= (String) authenticationToken.getPrincipal();
        System.out.println(principal);

        //根据身份信息查询相关数据库
        if ("zhangsan".equals(principal)) {
            //第一个参数:数据库中正确的用户名,第二个参数:数据库中正确的密码,第三个参数:提供当前Realm的名字
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("principal", "123", this.getName());
            return authenticationInfo;
        }
        return null;
    }
}
2、实现自定义Realm
public class TestCustomerRealmAuthentication {
    public static void main(String[] args) {

        //创建安全管理
        DefaultSecurityManager securityManager=new DefaultSecurityManager();

        //设置Realm
        securityManager.setRealm(new CustomerRealm());

        //使用安全工具使用安全管理
        SecurityUtils.setSecurityManager(securityManager);

        //使用安全工具获取subject
        Subject subject=SecurityUtils.getSubject();

        //创建token
        UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","123");
        try{
            securityManager.login(subject,token);
            System.out.println();
        }catch (UnknownAccountException e){
            System.out.println("用户名不存在");
            e.printStackTrace();
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
    }

}

3、使用md5+盐+多次散列

1、创建一个测试
public class TestShiroMD5 {
    public static void main(String[] args) {
        //创建一个MD5算法
        Md5Hash md5Hash=new Md5Hash("123");
        System.out.println(md5Hash.toHex());

        //使用MD5+salt
        Md5Hash md5Hash1=new Md5Hash("123","xo*56");
        System.out.println(md5Hash1.toHex());

        //使用MD5+salt+多次散列(默认是散列一次)
        Md5Hash md5Hash2=new Md5Hash("123","xo*56",1024);
        System.out.println(md5Hash2.toHex());
    }
}

输出结果:

202cb962ac59075b964b07152d234b70
da6e378e550da9efcc84976d0a8bc68b
825feb4279a620fd9bf50fa5850fa97c

2、创建Realm类CustomerMd5Realm继承AuthorizingRealm

使用md5 : credentialsMatcher.setHashAlgorithmName(“md5”);测试类中改变默认的credentialsMatcher

加盐 : 在自定义返回的SimpleAuthenticationInfo类的构造方法加入ByteSource.Util.bytes("xo*56")

散列多次 : credentialsMatcher.setHashIterations(1024);

public class CustomerMd5Realm extends AuthorizingRealm {
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取身份信息
        String principal= (String) authenticationToken.getPrincipal();

        //根据用户名查询数据库
        if(principal.equals("zhangsan")){
            return new SimpleAuthenticationInfo(principal,
                    "825feb4279a620fd9bf50fa5850fa97c",
                    ByteSource.Util.bytes("xo*56")  //加盐
                    ,this.getName());
        }
        return null;
    }
}
3、测试(这里只放入(MD5+salt+散列)
//测试Md5
public class TestCustomerMd5RealmAuthentication {
    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //设置Realm
        CustomerMd5Realm realm = new CustomerMd5Realm();
        securityManager.setRealm(realm);
        //设置Realm使用Hash凭证匹配器(默认是使用简单的credentialsMatcher)
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        //md5
        credentialsMatcher.setHashAlgorithmName("md5");
        //散列
        credentialsMatcher.setHashIterations(1024);

        realm.setCredentialsMatcher(credentialsMatcher);

        //将安全管理器注入安全工具
        SecurityUtils.setSecurityManager(securityManager);

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

        //认证
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");

        try {
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e) {
            System.out.println("该用户名不存在");
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
    }
}

2、授权

授权可简单理解为who对what(which)进行How操作:
  • Who,即主体(Subject),主体需要访问系统中的资源。
  • What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型资源实例,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。
  • How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。

这里直接上代码(接着上一部分的认证)

1、在测试类查看用户是否具有某些个角色或者资源的权限
//如果认证通过,进行授权控制
if(subject.isAuthenticated()){
    //1.基于角色权限控制
    System.out.println(subject.hasRole("admin"));

    //2.基于多角色权限控制,调用了两次doGetAuthorizationInfo
    System.out.println(subject.hasAllRoles(Arrays.asList("admin","user")));

    //3.是否具有其中一个角色
    boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
    System.out.println(Arrays.toString(booleans));

    //4.基于权限字符串的访问控制,资源标识符:操作:资源类型
    System.out.println("=================");
    System.out.println("权限"+subject.isPermitted("user:*:01"));
    System.out.println("权限"+subject.isPermitted("product:create:01"));

    //5.同时具有哪些权限
    System.out.println(subject.isPermittedAll("user:*:01","product:create:01"));

    //6.分别具有哪些权限
    boolean[] permitted = subject.isPermitted("user:*:01", "product:create:01");
    System.out.println(Arrays.toString(permitted));
}
2、Realm类的doGetAuthorizationInfo方法分配权限
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //拿到用户身份信息(用户名)
    String principal= (String) principalCollection.getPrimaryPrincipal();
    System.out.println("身份信息"+principal);

    //给用户添加角色权限
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    authorizationInfo.addRole("admin");
    authorizationInfo.addRole("user");

    //将数据库中查询权限信息赋值权限对象
    //表示将user模块的所有权限
    authorizationInfo.addStringPermission("user:*:*");
    //表示product模块的01对象进行create的权限
    authorizationInfo.addStringPermission("product:create:01");
    return authorizationInfo;
}
mpleAuthorizationInfo();
    authorizationInfo.addRole("admin");
    authorizationInfo.addRole("user");

    //将数据库中查询权限信息赋值权限对象
    //表示将user模块的所有权限
    authorizationInfo.addStringPermission("user:*:*");
    //表示product模块的01对象进行create的权限
    authorizationInfo.addStringPermission("product:create:01");
    return authorizationInfo;
}

本文代码主要仿照b站编程不良人的shiro教程
链接地址:https://www.bilibili.com/video/BV1uz4y197Zm/?p=12

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值