Shiro 介绍 + MD5 加密

什么是 Apache Shiro?

Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。

Shiro 能做什么事情?

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。

Shiro 架构

  1. Subject(用户): 访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;Subject 一词是一个专业术语,其基本意思是“当前的操作用户”。它是一个抽象的概念,可以是人,也可以是第三方进程或其他类似事物,如爬虫,机器人等。 在程序任意位置:Subject currentUser = SecurityUtils.getSubject(); 类似 Employee user = UserContext.getUser()一旦获得 Subject,你就可以立即获得你希望用 Shiro 为当前用户做的 90%的事情,如登录、登出、访问会话、执行授权检查等

  2. SecurityManager(安全管理器) 安全管理器,它是 shiro 功能实现的核心,负责与后边介绍的其他组件(认证器/授权器/缓存控制器)进行交互,实现 subject 委托的各种功能。有点类似于SpringMVC 中的 DispatcherServlet 前端控制器。

  3. Realms(数据源) Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。;可以把Realm 看成 DataSource,即安全数据源。执行认证(登录)和授权(访问控制)时,Shiro 会从应用配置的 Realm 中查找相关的比对数据。以确认用户是否合法,操作是否合理

  4. Authenticator Authenticator 用于认证,协调一个或者多个 Realm,从 Realm 指定的数据源取得数据之后进行执行具体的认证。

  5. Authorizer Authorizer 用户访问控制授权,决定用户是否拥有执行指定操作的权限。

  6. SessionManager Shiro 与生俱来就支持会话管理,这在安全类框架中都是独一无二的功能。即便不存在 web容器环境,shiro 都可以使用自己的会话管理机制,提供相同的会话 API。

  7. CacheManager 缓存组件,用于缓存认证信息等。

  8. Cryptography Shiro 提供了一个加解密的命令行工具 jar 包,需要单独下载使用。

Shiro 架构的核心(Subject,SecurityManager,Realms)

subject 主体 ,当前登录用户 (里面有个状态可以区分是否有登录)

securityManager 安全管理器,管理很多组件,分发调度

realm数据源 , 提供数据给shiro进行逻辑处理

Shiro 在应用中最常使用的两个功能:用户登录认证和访问授权

Shiro 认证概述

认证的过程即为用户的身份确认过程,所实现的功能就是我们所熟悉的登录验证,用户输入账号和密码提交到后台,后台通过访问数据库执行账号密码的正确性校验。

基于 ini 的 Shiro 认证

  1. 导入基本的 jar 包

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    <dependency>
    	<groupId>org.apache.shiro</groupId>
    	<artifactId>shiro-core</artifactId>
    	<version>1.2.2</version>
    </dependency>
    
  2. 编写 ini 配置文件:shiro.ini

    #用户的身份、凭据
    [users]
    zhangsan=555
    lisi=666
    
  3. 使用 Shiro 相关的 API 完成身份认证

    @Test
    public void testShiro() throws Exception {
        //加载 shiro.ini 配置文件,得到配置中的用户信息(账号+密码)
        IniSecurityManagerFactory factory =
            new IniSecurityManagerFactory("classpath:shiro.ini");
        //创建 Shiro 的安全管理器
        SecurityManager manager = factory.getInstance();
        //将创建的安全管理器添加到运行环境中
        SecurityUtils.setSecurityManager(manager);
        //获取登录的用户主体对象
        Subject subject = SecurityUtils.getSubject();
        System.out.println("登录前的认证状态: " + subject.isAuthenticated());//false //创建登录用户的身份凭证
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "555");
        try {//登录认证
            subject.login(token);
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误");
        }
        System.out.println("登录后的认证状态:" + subject.isAuthenticated());//true
    }
    

登录失败存在两种情况

  1. 账号错误

    org.apache.shiro.authc.UnknownAccountException

  2. 密码错误

    org.apache.shiro.authc.IncorrectCredentialsException

自定义 Realm

Reaml 的继承体系

在继承体系中的每个类所能够实现的功能不一样,在后面的开发中,我们通常需要使用到缓存,认证和授权所有的功能,所以选择继承 AuthorizingRealm。

public class CrmRealm extends 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;
    }
}

通过配置修改 SecurityManager 中的默认 Realm 的使用 shiro.ini

#自定义的 Realm 信息
crmRealm=cn.wolfcode.crm.shiro.CRMRealm
#将 crmRealm 设置到当前的环境中
securityManager.realms=$crmRealm

集成 Shiro 认证

添加依赖(Shiro 常用的依赖都在这里)

<properties>
	<shiro.version>1.5.2</shiro.version>
</properties>
<!--shiro 核心-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 的 Web 模块-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 和 Spring 集成-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 底层使用的 ehcache 缓存-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 依赖的日志包-->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<!--shiro 核心-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 的 Web 模块-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 和 Spring 集成-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 底层使用的 ehcache 缓存-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!--shiro 依赖的日志包-->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<!--shiro 依赖的工具包-->
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
<!--Freemarker 的 shiro 标签库-->
<dependency>
    <groupId>net.mingsoft</groupId>
    <artifactId>shiro-freemarker-tags</artifactId>
    <version>1.0.1</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
        </exclusion>
    </exclusions>
</dependency>

shiroFilter过虑器

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

shiro 过虑器,DelegatingFilterProx 会从 spring 容器中找 shiroFilter,所以过滤器的生命周期还是交给 Spring 进行管理的。Shiro 中定义了多个过滤器来完成不同的预处理操作。

anon: 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon”

authc: 表示需要认证(登录)才能使用;示例“/**=authc” 主要属性:

usernameParam:表单提交的用户名参数名( username);

passwordParam:表单提交的密码参数名(password);

rememberMeParam:表单提交的密码参数名(rememberMe);

loginUrl:登录页面地址(/login.jsp);

successUrl:登录成功后的默认重定向地址;

failureKeyAttribute:登录失败后错误信息存储 key(shiroLoginFailure);

authcBasic: Basic HTTP 身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application);

roles:角色授权拦截器,验证用户是否拥有资源角色;示例“/admin/=roles[admin]”

**perms:**权限授权拦截器,验证用户是否拥有资源权限;示例“/employee/input=perms[“user:update”]” **user:**用户拦截器,用户已经身份验证/记住我登录的都可;示例“/index=user”

**logout:**注销拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”

**port:**端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为 80 并重定向到该 80 端口,其他路径/参数等都一样

rest: rest 风格拦截器,自动根据请求方法构建权限字符(GET=read,POST=create,PUT=update,DELETE=delete, HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串; 示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹isPermittedAll);

**ssl:**SSL 拦截器,只有请求协议是 https 才能通过;否则自动跳转会 https 端口(443);其他和 port 拦截器一样;

shiro.xml 配置文件

为了单独对 Shiro 相关的配置进行管理,我们分离出一个 shiro.xml 配置文件,最后在 mvc.xml 中引入

mvc.xml

<import resource="classpath:shiro.xml"/>

shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <context:component-scan base-package="cn.wolfcode.shiro"/>

    <!--shiro过滤器  spring管理  指定系统资源需要使用的具体过滤器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--引用指定的安全管理器-->
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.html"/>
        <property name="filterChainDefinitions">
            <value>
                /login.html=anon
                /login.do=anon
                /js/**=anon
                /images/**=anon
                /css/**=anon
                /logout.do=logout
                /**=authc
            </value>
        </property>
    </bean>

    <!--安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="crmRealm"/>
        <!--注册缓存器-->
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <!--凭证匹配器-->
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <!--指定加密算法-->
        <property name="hashAlgorithmName" value="MD5"/>
    </bean>

    <!--开启shiro注解扫描器-->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- 设置配置文件 -->
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>
</beans>

Shiro 授权概述

系统中的授权功能就是为用户分配相关的权限,只有当用户拥有相应的权限后,才能访问对应的资源。如果系统中无法管理用户的权限,那么将会出现非常客户信息泄露,数据没恶意篡改等问题,所以在绝大多数的应用中,我们都会有权限管理模块。

Shiro 授权方式分为三种:

  1. 编程式 通过写 if/else 授权代码块完成

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole("admin")) {
    	//有权限
    } else {
    	//无权限
    }
    
  2. 注解式 通过在执行的 Java 方法上放置相应的注解完成

    @RequiresRoles("admin")
    @RequiresPermissions("user:create")
    public void hello() {
    	//有权限
    }
    
  3. JSP 标签(shiro 自带)或 freemarker 的标签(第三方) 在 JSP 页面通过相应的标签完成

    <shiro:hasRole name="admin">
    	<!-- 有权限 -->
    </shiro:hasRole>
    

Shiro 标签

在前端页面上,我们通常可以根据用户拥有的权限来显示具体的页面,如:用户拥有删除员工的权限,页面上就把删除按钮显示出来,否则就不显示删除按钮,通过这种方式来细化权限控制。

要能够实现上面的控制,需要使用 Shiro 中提供的相关标签,标签的使用步骤如下:

  1. 引入相关的 jar 包

    <dependency>
        <groupId>net.mingsoft</groupId>
        <artifactId>shiro-freemarker-tags</artifactId>
        <version>1.0.0</version>
    </dependency>
    
  2. 前端页面我们选择的是 freemarker,而默认 freemarker 是不支持 shiro 标签的,所以需要对其功能做拓展

    可以理解为注册 shiro 的标签,达到在 freemarker 页面中使用的目的。

    public class MyFreeMarkerConfig extends FreeMarkerConfigurer{ 
        @Override
    	public void afterPropertiesSet() throws IOException, TemplateException{
            //继承之前的属性配置,这不不能省
            super.afterPropertiesSet();
            Configuration cfg = this.getConfiguration();
            cfg.setSharedVariable("shiro", new ShiroTags());//shiro 标签
    	} 
    }
    
  3. 在 mvc.xml 中将 MyFreeMarkerCon_g 设置成当前环境中使用的配置对象

        <!-- 注册 FreeMarker 配置类 (配置自定义的配置类,带有shiro标签功能)-->
        <bean class="cn.wolfcode.shiro.MyFreeMarkerConfig">
            <!-- 配置 FreeMarker 的文件编码 -->
            <property name="defaultEncoding" value="UTF-8" />
            <!-- 配置 FreeMarker 寻找模板的路径 -->
            <property name="templateLoaderPath" value="/WEB-INF/views/" />
        </bean>
    

有了上面的准备工作后,我们就可以在 freemarker 页面中使用 shiro 相关的标签来对页面显示做控制了。

shirofreemarker 常用标签:

  1. authenticated 标签:已认证通过的用户。

    <@shiro.authenticated></@shiro.authenticated>
    
  2. notAuthenticated 标签:未认证通过的用户。与 authenticated 标签相对。

    <@shiro.notAuthenticated></@shiro.notAuthenticated>
    
  3. principal 标签:输出当前用户信息,通常为登录帐号信息

    <@shiro.principal property="name" />
    
  4. hasRole 标签:验证当前用户是否属于该角色 ,

    <@shiro.hasRole name=”admin”>Hello admin!</@shiro.hasRole>
    
  5. hasAnyRoles 标签:验证当前用户是否属于这些角色中的任何一个,角色之间逗号分隔 ,

    <@shiro.hasAnyRoles name="admin,user,operator">Hello admin! </@shiro.hasAnyRoles>
    
  6. hasPermission 标签:验证当前用户是否拥有该权限 ,

    <@shiro.hasPermission name="/order:*">订单/@shiro.hasPermission>
    

示例

<@shiro.hasPermission name="employee:delete">
    <a href="#" class="btn btn-danger btn-xs btn-delete"data-href="/employee/delete.do?id=${entity.id}">
    	<span class="glyphicon glyphicon-trash"></span> 删除
    </a>
</@shiro.hasPermission>

MD5 加密

加密的目的是从系统数据的安全考虑,如,用户的密码,如果我们不对其加密,那么所有用户的密码在数据库中都是明文,只要有权限查看数据库的都能够得知用户的密码,这是非常不安全的。所以,对用用户来说非常机密的数据,我们都应该想到使用加密技术,这里我们采用的是 MD5 加密。

在 Shiro 中继承了 MD5 的算法,所以可以直接使用它来实现对密码进行加密。

@Test
public void testMD5() throws Exception{
    Md5Hash hash = new Md5Hash("1");
    System.out.println(hash);//c4ca4238a0b923820dcc509a6f75849b
}

使用 Md5Hash 直接对数据进行加密

MD5 加密的数据如果一样,那么无论在什么时候加密的结果都是一样的,所以,相对来说还是不够安全,但是别怕,我们可以对数据加“盐”。同样的数据加不同的“盐”之后就是千变万化的,因为我们不同的人加的“盐”都不一样。这样得到的结果相同率也就变低了。

@Test
public void testMD5() throws Exception{ 
    Md5Hash hash = new Md5Hash("1","admin");
    System.out.println(hash);//e00cf25ad42683b3df678c61f42c6bda
}

Md5Hash()构造方法中的第二个参数就是对加密数据添加的“盐”,加密之后的结果也和之前不一样了。

如果还觉得不够安全,我们还可以通过加密次数来增加 MD5 加密的安全性。

@Test
public void testMD5() throws Exception{ 
	Md5Hash hash = new Md5Hash("1","admin",3);
    System.out.println(hash);//f3559efea469bd6de83d27d4284b4a7a
}

上面指定对密码进行 3 次 MD5 加密,在开发中可以根据实际情况来指定加密次数。

EhCache缓存机制

在请求中一旦进行权限的控制

,如:

@RequiresPermissions(“employee:view”)

<shiro:hasPermission name=“employee:input”>

都去到 Realm 中的 doGetAuthorizationInfo 方法进行授权,我们授权信息应该要从数据库中查询的。 如果每次授权都要去查询数据库就太频繁了,性能不好. 而且用户登陆后,授权信息一般很少变动,所有我们可以在第一次授权后就把这些授权信息存到缓存中,下一次就直接从缓存中获取,避免频繁访问数据库。

Shiro 中没有实现自己的缓存机制,只提供了一个可以支持具体缓存实现(如:Hazelcast, Ehcache, OSCache, Terracotta, Coherence, GigaSpaces, JBossCache 等)的抽象 API 接口,这样就允许 Shiro用户根据自己的需求灵活地选择具体的 CacheManager。这里我们选择使用 EhCache。

实现步骤:

  1. 添加依赖

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>1.2.2</version>
    </dependency>
    
  2. 配置缓存管理器并引用缓存管理器

    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- 设置配置文件 -->
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>
    
  3. 添加 ehcache 配置文件:shiro-ehcache.xml

    <ehcache>
        <defaultCache>
            maxElementsInMemory="1000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    

    Ehcache 配置属性说明

    name: 缓存名称。

    maxElementsInMemory: 缓存最大个数。

    eternal:对象是否永久有效,一但设置了,timeout 将不起作用。

    timeToIdleSeconds: 设置对象在失效前的允许闲置时间(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。

    timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于

    创建时间和失效时间之间。仅当 eternal=false 对象不是永久有效时使用,默认是 0.,也就是对

    象存活时间无穷大。

    over owToDisk:当内存中对象数量达到 maxElementsInMemory 时,Ehcache 将会对象写到磁盘中。 diskSpoolBu erSizeMB:这个参数设置 DiskStore(磁盘缓存)的缓存区大小。默认是 30MB。每个 Cache 都应该有自己的一个缓冲区。

    maxElementsOnDisk:硬盘最大缓存个数。

    diskPersistent:是否缓存虚拟机重启期数据Whether the disk store persists between restarts of the Virtual Machine. The default value is false.

    diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是 120 秒。

    memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会

    根据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为 FIFO(先进

    先出)或是 LFU(较少使用)。

    clearOnFlush:内存数量最大时是否清除。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小云很优秀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值