Apache Shiro(一)入门

一、Shiro是什么?

shrio框架是一个安全框架,可以用在JavaEE和JavaSE的环境中。可以方便的对用户进行登录验证,权限管理、加密、会话管理、与Web继承、缓存等。

二、Shiro的组件

在这里插入图片描述

subject:代表所有访问该程序的东西。包括人,其他程序(爬虫)等
Security Manager:安全管理器,shrio的核心组件
Authenticator:认证器
Authorizer:授权器
Realm:领域。访问数据的地方
Cryptography:加密

三、Shiro入门案例

引入依赖

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

示例1、访问ini文件的数据,模拟登录

(1)ini文件

[users]
zh=123456
ls=123456

(2)测试类

public class Test {
    public static void main(String[] args) {
//       获取安全管理器
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
//        获取Realm。Realm可以自己写符合自己功能需求的Realm
        IniRealm iniRealm = new IniRealm("classpath:data.ini");
//        使用安全管理器管理realm
        securityManager.setRealm(iniRealm);
//        将安全管理器绑定到SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
//        获取subject对象,就是用户对象,代表一切访问对象
        Subject subject = SecurityUtils.getSubject();
        
        UsernamePasswordToken upToken=new UsernamePasswordToken("zh","123456");
        try{
//            执行login
            subject.login(upToken);
            System.out.println("登录成功");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("密码或账户有问题");
        }

    }
}

示例2、写自己的Realm,模拟登录

(1) MyRealm.java

public class MyRealm extends AuthorizingRealm {
    private UserService userService=new UserService();

//    当执行授权时,执行该方法
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("-------------------授权方法--------------------");
//        PrincipalCollection:可以获取认证方法传递的信息
        User user =(User) principalCollection.getPrimaryPrincipal();
//        查询权限
        List<String> permissionByUserId =
                userService.findPermissionByUserId(user.getId());
//        绑定权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissionByUserId);

        return simpleAuthorizationInfo;
    }

//    当执行认证时,调用该方法
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("---------------------认证方法---------------------");
//        AuthenticationToken中包含有subject.login()方法传递的登录信息
        String username = authenticationToken.getPrincipal().toString();//getPrincipal()方法得到的是username
        System.out.println(username);

        User byLoginname = userService.findByLoginname(username);
      /*SimpleAuthenticationInfo():该方法中有三个参数
        *principal:登录的用户名或者是其他有效的认证信息,比如查询出来的整个对象,可以被登录成功后在登录方法中获取,还可以被授权方法获取
        * credentials:密码
        * realmName:当前realm的对象
      * */
//        该方法中只根据username查出来了整个用户对象,具体的密码比对就交给了shiro的安全管理器,就是这个方法
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(byLoginname,byLoginname.getPassword(),this.getName());

        return simpleAuthenticationInfo;
    }
}

(2)service层:模拟查询数据库

public class UserService {
    public User findByLoginname(String loginname) {
        System.out.println(loginname);
        switch (loginname){
            case "admin": return new User(1,"admin","12345","管理员");
            case "zyl": return new User(2,"zyl","12345","张宇磊");
            case "cxj": return new User(3,"cxj","12345","程熊洁");
            default:return null;
        }
    }

    public List<String> findPermissionByUserId(int id) {
        List<String> list=new ArrayList<>();
        if(id==1){
            list.add("user:query");
            list.add("user:delete");
            list.add("user:update");
            list.add("user:insert");
        }else if(id==2) {
            list.add("user:query");
        }else if(id==3){
            list.add("user:delete");
            list.add("user:update");
        }
        return list;
    }
}

(3)实体类 User.java

public class User {
    private int id;
    private String username;
    private String password;
    private String role;

    public User(int id, String username, String password, String role) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.role = role;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", role='" + role + '\'' +
                '}';
    }
}

(4) 测试类

public class MyRealmTest {
    public static void main(String[] args) {
//        获取安全管理器
        DefaultSecurityManager manager = new DefaultSecurityManager();
//        获取Realm对象
        MyRealm myRealm = new MyRealm();
//        将realm交给安全管理器管理
        manager.setRealm(myRealm);

//        将安全管理器绑定到SecurityUtils
        SecurityUtils.setSecurityManager(manager);
//        获取访问对象
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("admin","1245");
//       执行登录,会将登录信息发送到Realm
        subject.login(usernamePasswordToken);
//      Realm的认证方法中返回的信息,可以通过getPrincipal()获取
        Object principal = subject.getPrincipal();

        System.out.println(principal);
//        判断是否认证成功
        System.out.println("是否认证成功"+subject.isAuthenticated());
//        判断是否用户是否有该权限
        System.out.println("是否授权成功"+subject.isPermittedAll("user:select"));
    }
}

四、Shiro认证流程

在这里插入图片描述

	首先调用subject.login()方法执行登录,其会自动委托给Security Manager,但是访问之前要将安全管理器交给SecurityUtils管理。SecutiryManager负责真正得身份验证逻辑,会自动调用Authenticator进行身份验证。Authenticator是真正的身份验证者,是Shiro API的核心的身份入口点,此处可以放入自定义的内容。Authenticator可能会调用Authentication Strategy进行多Realm认证。默认 ModularRealmAuthenticator 会调用AuthenticationStrategy 进行多 Realm 身份验证。Authenticator会将token中的信息传递给对应的Realm进行认证授权。可以配置多Realm认证。

五、Shiro的加密功能

	shiro的加密功能是为了给密码加密,shiro加密的底层使用的是MD5算法,加密后的结果是不可逆的。

1、输入的密码如何同数据库中的密码进行比对?

	过程:比对的过程发生在Realm的doGetAuthenticationInfo()方法中,先根据用户名找
到数据库中的用户信息。然后将用户信息中的密码放在SimpleAuthenticationInfo()
方法中,有的数据库中放有加密用的盐(盐是什么下文解释),也放到SimpleAuthenticationInfo()
,这样shrio就会将登录时传入token中的密码经过加盐,然后经过md5加密,同数据库
中的密码进行比对,正确就登录成功。

2、MD5加密是如何加密的?
示例:

public class Test2 {
    public static void main(String[] args) {
//      我们使用Md5Hash进行加密,它有三个构造方法
//        1、只传入需要加密的内容
        Md5Hash md5Hash1 = new Md5Hash("123456");
//        2、传入需要加密的内容以及“盐”,盐的作用就是给需要加密的内容增加一些字段,使加密的结果更安全
        Md5Hash md5Hash2 = new Md5Hash("123456", "UUID");
//        3、传入需要加密的内容、盐以及散列次数(值自己定,值代表散列次数),散列是将加密后的结果通过散列算法再次排序
        Md5Hash md5Hash3 = new Md5Hash("123456", "UUID", 1024);
        System.out.println(md5Hash3);
    }
}

3、shiro中的加密功能怎么使用?
①在上边示例的基础上,给Realm加一个密码匹配器:

public class MyRealmTest {
    public static void main(String[] args) {
//        获取安全管理器
        DefaultSecurityManager manager = new DefaultSecurityManager();
//        获取Realm对象
        MyRealm myRealm = new MyRealm();
        
*//      给realm配置密码匹配器
//        先创建匹配器
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//        设置散列次数
        matcher.setHashIterations(1024);
//        设置加密法则
        matcher.setHashAlgorithmName("MD5");
//        有疑问为什么加盐没有设置吗?盐一般存放在数据库中,在Realm中的认证过程中使用
        //      给realm配置密码匹配器
        myRealm.setCredentialsMatcher(matcher);*

//        将realm交给安全管理器管理
        manager.setRealm(myRealm);

//        将安全管理器绑定到SecurityUtils
        SecurityUtils.setSecurityManager(manager);
//        获取访问对象
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("admin","123456");
//       执行登录,会将登录信息发送到Realm
        subject.login(usernamePasswordToken);
//      Realm的认证方法中返回的信息,可以通过getPrincipal()获取
        Object principal = subject.getPrincipal();

        System.out.println(principal);
//        判断是否认证成功
        System.out.println("是否认证成功"+subject.isAuthenticated());
//        判断是否用户是否有该权限
        System.out.println("是否授权成功"+subject.isPermittedAll("user:select"));
    }
}

②MyRealm中的认证方法中用到了 “盐”

public class MyRealm extends AuthorizingRealm {
    private UserService userService=new UserService();

//    当执行授权时,执行该方法
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("-------------------授权方法--------------------");
//        PrincipalCollection:可以获取认证方法传递的信息
        User user =(User) principalCollection.getPrimaryPrincipal();
//        查询权限
        List<String> permissionByUserId =
                userService.findPermissionByUserId(user.getId());
//        绑定权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissionByUserId);

        return simpleAuthorizationInfo;
    }

//    当执行认证时,调用该方法
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("---------------------认证方法---------------------");
//        AuthenticationToken中包含有subject.login()方法传递的登录信息
        String username = authenticationToken.getPrincipal().toString();//getPrincipal()方法得到的是username
        System.out.println(username);

        User byLoginname = userService.findByLoginname(username);
      /*SimpleAuthenticationInfo():该方法中有四个参数
        *principal:登录的用户名或者是其他有效的认证信息,比如查询出来的整个对象,可以被登录成功后在登录方法中获取,还可以被授权方法获取
        * credentials:密码
        * credentialsSalt:盐
        * realmName:当前realm的对象
      * */
//        该方法中只根据username查出来了整个用户对象,具体的密码比对就交给了shiro的安全管理器,就是这个方法,会将查询出来的密码和token中的密码进行比对
        ByteSource salt = ByteSource.Util.bytes("UUID");
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(byLoginname,byLoginname.getPassword(),salt,this.getName());

        return simpleAuthenticationInfo;
    }
}

③模拟的数据 信息中密码要改成加密后的

public class UserService {
    public User findByLoginname(String loginname) {
        System.out.println(loginname);
        switch (loginname){
            case "admin": return new User(1,"admin","a25896f7da05e18735f037dfd556ba40","管理员");
            case "zyl": return new User(2,"zyl","a25896f7da05e18735f037dfd556ba40","张宇磊");
            case "cxj": return new User(3,"cxj","a25896f7da05e18735f037dfd556ba40","程熊洁");
            default:return null;
        }
    }

    public List<String> findPermissionByUserId(int id) {
        List<String> list=new ArrayList<>();
        if(id==1){
            list.add("user:query");
            list.add("user:delete");
            list.add("user:update");
            list.add("user:insert");
        }else if(id==2) {
            list.add("user:query");
        }else if(id==3){
            list.add("user:delete");
            list.add("user:update");
        }
        return list;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值