Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】

什么是Shiro

shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户受权java

spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
shiro不依赖于spring,shiro不只能够实现 web应用的权限管理,还能够实现c/s系统,分布式系统权限管理,shiro属于轻量框架,愈来愈多企业项目开始使用shiro。web

Shiro架构:算法

这里写图片描述

  • subject:主体,能够是用户也能够是程序,主体要访问系统,系统须要对主体进行认证、受权。
  • securityManager:安全管理器,主体进行认证和受权都 是经过securityManager进行。
  • authenticator:认证器,主体进行认证最终经过authenticator进行的。
  • authorizer:受权器,主体进行受权最终经过authorizer进行的。
  • sessionManager:web应用中通常是用web容器对session进行管理,shiro也提供一套session管理的方式。
  • SessionDao: 经过SessionDao管理session数据,针对个性化的session数据存储须要使用sessionDao。
  • cache Manager:缓存管理器,主要对session和受权数据进行缓存,好比将受权数据经过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
  • realm:域,领域,至关于数据源,经过realm存取认证、受权相关数据。

cryptography:密码管理,提供了一套加密/解密的组件,方便开发。好比提供经常使用的散列、加/解密等功能。spring

  • 好比md5散列算法。

为何使用Shiro

咱们在使用URL拦截的时候,要将全部的URL都配置起来,繁琐、不易维护数据库

而咱们的Shiro实现系统的权限管理,有效提升开发效率,从而下降开发成本。apache

Shiro认证

导入jar包

咱们使用的是Maven的坐标就好了缓存

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>1.2.3</version>
        </dependency>

固然了,咱们也能够把Shiro相关的jar包所有导入进去安全

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>1.2.3</version>
</dependency>

Shiro认证流程

这里写图片描述

经过配置文件建立工厂

这里写图片描述

// 用户登录和退出
    @Test
    public void testLoginAndLogout() {

        // 建立securityManager工厂,经过ini配置文件建立securityManager工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-first.ini");

        // 建立SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 将securityManager设置当前的运行环境中
        SecurityUtils.setSecurityManager(securityManager);

        // 从SecurityUtils里边建立一个subject
        Subject subject = SecurityUtils.getSubject();

        // 在认证提交前准备token(令牌)
        // 这里的帐号和密码 未来是由用户输入进去
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
                "111111");
        try {
            // 执行认证提交
            subject.login(token);
        } catch (AuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 是否定证经过
        boolean isAuthenticated = subject.isAuthenticated();

        System.out.println("是否定证经过:" + isAuthenticated);

        // 退出操做
        subject.logout();

        // 是否定证经过
        isAuthenticated = subject.isAuthenticated();

        System.out.println("是否定证经过:" + isAuthenticated);

    }

这里写图片描述

小结

ModularRealmAuthenticator做用进行认证,须要调用realm查询用户信息(在数据库中存在用户信息)
ModularRealmAuthenticator进行密码对比(认证过程)。
realm:须要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),若是查到用户返回认证信息,若是查询不到返回nullmarkdown

自定义realm

从第一个认证程序咱们能够看见,咱们所说的流程,是认证器去找realm去查询咱们相对应的数据。而默认的realm是直接去与配置文件来比对的,通常地,咱们在开发中都是让realm去数据库中比对。
所以,咱们须要自定义realmsession

这里写图片描述

public class CustomRealm extends AuthorizingRealm {

    // 设置realm的名称
    @Override
    public void setName(String name) {
        super.setName("customRealm");
    }

    // 用于认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        // token是用户输入的
        // 第一步从token中取出身份信息
        String userCode = (String) token.getPrincipal();

        // 第二步:根据用户输入的userCode从数据库查询
        // ....


        // 若是查询不到返回null
        //数据库中用户帐号是zhangsansan
        /*if(!userCode.equals("zhangsansan")){// return null; }*/


        // 模拟从数据库查询到密码
        String password = "111112";

        // 若是查询到返回认证信息AuthenticationInfo

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                userCode, password, this.getName());

        return simpleAuthenticationInfo;
    }

    // 用于受权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // TODO Auto-generated method stub
        return null;
    }

}

配置realm

须要在shiro-realm.ini配置realm注入到securityManager中。

这里写图片描述

测试自定义realm

同上边的入门程序,须要更改ini配置文件路径:

同上边的入门程序,须要更改ini配置文件路径:
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-realm.ini");

散列算法

咱们若是知道md5,咱们就会知道md5是不可逆的,可是若是设置了一些安全性比较低的密码:111111…即时是不可逆的,但仍是能够经过暴力算法来获得md5对应的明文…

建议对md5进行散列时加salt(盐),进行加密至关 于对原始密码+盐进行散列。\

正常使用时散列方法:

  • 在程序中对原始密码+盐进行散列,将散列值存储到数据库中,而且还要将盐也要存储在数据库中。

测试:

public class MD5Test {

    public static void main(String[] args) {

        //原始 密码 
        String source = "111111";
        //盐
        String salt = "qwerty";
        //散列次数
        int hashIterations = 2;
        //上边散列1次:f3694f162729b7d0254c6e40260bf15c
        //上边散列2次:36f2dfa24d0a9fa97276abbe13e596fc


        //构造方法中:
        //第一个参数:明文,原始密码 
        //第二个参数:盐,经过使用随机数
        //第三个参数:散列的次数,好比散列两次,至关 于md5(md5(''))
        Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);

        String password_md5 =  md5Hash.toString();
        System.out.println(password_md5);
        //第一个参数:散列算法 
        SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
        System.out.println(simpleHash.toString());
    }

}

自定义realm支持md5

自定义realm

public class CustomRealmMd5 extends AuthorizingRealm {

    // 设置realm的名称
    @Override
    public void setName(String name) {
        super.setName("customRealmMd5");
    }

    // 用于认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        // token是用户输入的
        // 第一步从token中取出身份信息
        String userCode = (String) token.getPrincipal();

        // 第二步:根据用户输入的userCode从数据库查询
        // ....

        // 若是查询不到返回null
        // 数据库中用户帐号是zhangsansan
        /* * if(!userCode.equals("zhangsansan")){// return null; } */

        // 模拟从数据库查询到密码,散列值
        String password = "f3694f162729b7d0254c6e40260bf15c";
        // 从数据库获取salt
        String salt = "qwerty";
        //上边散列值和盐对应的明文:111111

        // 若是查询到返回认证信息AuthenticationInfo
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                userCode, password, ByteSource.Util.bytes(salt), this.getName());

        return simpleAuthenticationInfo;
    }

    // 用于受权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // TODO Auto-generated method stub
        return null;
    }

}

配置文件:

这里写图片描述

测试:

// 自定义realm实现散列值匹配
    @Test
    public void testCustomRealmMd5() {

        // 建立securityManager工厂,经过ini配置文件建立securityManager工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-realm-md5.ini");

        // 建立SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 将securityManager设置当前的运行环境中
        SecurityUtils.setSecurityManager(securityManager);

        // 从SecurityUtils里边建立一个subject
        Subject subject = SecurityUtils.getSubject();

        // 在认证提交前准备token(令牌)
        // 这里的帐号和密码 未来是由用户输入进去
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
                "222222");

        try {
            // 执行认证提交
            subject.login(token);
        } catch (AuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 是否定证经过
        boolean isAuthenticated = subject.isAuthenticated();

        System.out.println("是否定证经过:" + isAuthenticated);

    }
相关文章
相关标签/搜索