专栏首页吴伟祥Shiro集成应用 原

Shiro集成应用 原

Simple. Java. Security.

       Shiro项目始于2003年初,当时它叫JSecurity项目,当时对于Java应用开发人员没有太多的安全替代方案,始终被一个叫JAAS(Java认证/授权服务)束缚着,但是JAAS缺点太多了,如它的授权机制太拙劣,用起来让人沮丧,又一方面JAAS跟虚拟机层面的安全问题关系非常紧密,如判断JVM中判断是否允许装入一个类等,还有加密问题,JAVA中的密码架构又是让人难以理解。于是Jsecurity就诞生了,后来更名为Shiro。

       直到2008年Shiro加入到了APACHE软件基金会,直到现在它叫Apache Shiro。

〇、Apache Shiro官网

一、什么是Shiro

Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能:

· 认证 - 用户身份识别,常被称为用户“登录”;

· 授权 - 访问控制;

· 密码加密 - 保护或隐藏数据防止被偷窥;

· 会话管理 - 每用户相关的时间敏感的状态。

对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro要简单的多。

二、Shiro的架构介绍

首先,来了解一下Shiro的三个核心组件:Subject, SecurityManager 和 Realms. 如下图:  

Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。  Subject代表了当前用户的安全操作

SecurityManager则管理所有用户的安全操作。 SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。  Realm: Realm充当了Shiro应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。  从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。  Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。 Shiro完整架构图:

最简单的一个Shiro应用:

1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;

2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

三、Shiro与Spring的集成

1、在web.xml配置shiroFilter

<!--添加ShiroFilter -->
   <filter>
       <!-- 这里filter-name必须对应applicationContext-shiro.xml中定义的<bean id="shiroFilter"/>一致,DelegatingFilterProxy会自动到Spring容器中查找名字为shiroFilter的bean并把filter请求交给它处理 -->
       <filter-name>shiroFilter</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
       <async-supported>true</async-supported>
       <init-param>
           <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
           <param-name>targetFilterLifecycle</param-name>
           <param-value>true</param-value>
       </init-param>
   </filter>
   <filter-mapping>
       <filter-name>shiroFilter</filter-name>
       <!--保证所有的可控请求都经过Shiro的过滤-->
       <url-pattern>/*</url-pattern>
   </filter-mapping>

2、在resources下添加applicationContext-shiro.xml配置 

<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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- =========================================================
         Shiro Core Components - Not Spring Specific
         ========================================================= -->
    <!-- Shiro's main business-tier object for web-enabled applications
         (use DefaultSecurityManager instead when there is no web environment)-->

    <!--1.配置securityManager   安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"/>

        <property name="realms">
            <list>
                <ref bean="firstRealm"/>
                <ref bean="secondRealm"/>
            </list>
        </property>

        <property name="rememberMeManager.cookie.maxAge" value="100"></property>
    </bean>

    <!-- Let's use some enterprise caching support for better performance.  You can replace this with any enterprise
         caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->

    <!--2.配置CacheManager    缓存管理器-->
    <!--2.1 加入 ehcache.xml配置文件-->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!--   ①可以用企业的一些缓存产品 来实现更好的性能
             Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
                will be creaed with a default config:
                <property name="cacheManager" ref="ehCacheManager"/> -->
        <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
             a specific Ehcache configuration to be used, specify that here.  If you don't, a default
             will be used.: -->
        <!--  ②直接指定ehCacheManager的配置文件-->
        <property name="cacheManagerConfigFile" value="classpath:conf/ehcache.xml"/>
    </bean>


    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"/>
        </property>
    </bean>

    <!-- Used by the SecurityManager to access security data (users, roles, etc).
         Many other realm implementations can be used too (PropertiesRealm,
         LdapRealm, etc. -->

    <!--3.配置Realm -->
    <!--3.1配置com.zhaogang.webapp.shiro.realm.AuthorizingRealm-->
    <bean id="firstRealm" class="com.zhaogang.webapp.shiro.realm.FirstRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="SHA-256"></property>
                <property name="hashIterations" value="2"></property>
            </bean>
        </property>
    </bean>

    <bean id="secondRealm" class="com.zhaogang.webapp.shiro.realm.SecondRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="SHA-1"></property>
                <property name="hashIterations" value="1024"></property>
            </bean>
        </property>
    </bean>

    <!-- =========================================================
         Shiro Spring-specific integration
         ========================================================= -->
    <!-- Post processor that automatically invokes init() and destroy() methods
         for Spring-configured Shiro objects so you don't have to
         1) specify an init-method and destroy-method attributes for every bean
            definition and
         2) even know which Shiro objects require these methods to be
            called. -->

    <!--4.配置Life cycle Bean Post Processor :可以自动的来调用配置在spring IOC容器中 shiro bean的生命周期方法-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
         the lifecycleBeanProcessor has run: -->
    <!--5.启用IOC容器中 使用shiro 注解,但必须在配置了lifecycleBeanProcessor之后才可以使用-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- Secure Spring remoting:  Ensure any Spring Remoting method invocations can be associated
         with a Subject for security checks. -->
    <!--<bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">-->
    <!--<property name="securityManager" ref="securityManager"/>-->
    <!--</bean>-->

    <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -
         web.xml uses the DelegatingFilterProxy to access this bean.  This allows us
         to wire things with more control as well utilize nice Spring things such as
         PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->


    <!--6.配置shiroFilter-->
    <!--id必须和web.xml 文件中的 DelegatingFilterProxy的  <filter-name> 一致-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login"/>
        <!--<property name="successUrl" value="/list"/>-->
        <property name="unauthorizedUrl" value="/unauthorized"/>
        <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean
             defined will be automatically acquired and available via its beanName in chain
             definitions, but you can perform overrides or parent/child consolidated configuration
             here if you like: -->
        <!-- <property name="filters">
            <util:map>
                <entry key="aName" value-ref="someFilterPojo"/>
            </util:map>
        </property> -->

        <!--配置哪些页面需要受保护,以及访问这些页面的权限-->
        <property name="filterChainDefinitions">
            <value>
                /statics/**         = anon
                /login              = anon
                /logout             = logout
                /loginAuth          = anon
                /register           = anon
                <!--/**           = urlauthc-->
                /**                 = anon
            </value>
        </property>
    </bean>

    <!--
        filterChainDefinitions参数说明,注意其验证顺序是自上而下
        =================================================================================================
        anon        org.apache.shiro.web.filter.authc.AnonymousFilter
        authc       org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        authcBasic  org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
        perms       org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
        port        org.apache.shiro.web.filter.authz.PortFilter
        rest        org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
        roles       org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
        ssl         org.apache.shiro.web.filter.authz.SslFilter
        user        org.apache.shiro.web.filter.authc.UserFilter
        =================================================================================================
        anon: 例子/admins/**=anon 没有参数,表示可以匿名使用。
        authc: 例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
        user: 例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
   
        perms: 例子/admins/user/**=perms[user:add:*]
                例如/admins/user/**=perms["user:add:*,user:modify:*"]
        多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
   
         roles: 例子/admins/user/**=roles[admin]
                例如admins/user/**=roles["admin,guest"],
        每个参数通过才算通过,相当于hasAllRoles()方法。
   
   
        rest:  例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,
        其中method为post,get,delete等。
   
        port:  例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,
        其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
        authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
        ssl:  例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
   
   
        注:   anon,authcBasic,auchc,user是认证过滤器,
              perms,roles,ssl,rest,port是授权过滤器
        =================================================================================================
    -->

</beans>

四、认证与授权的实现

自定义的Realm类

package com.zhaogang.webapp.shiro.realm;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.HashSet;
import java.util.Set;

public class FirstRealm extends org.apache.shiro.realm.AuthorizingRealm {

    private final static Logger logger = LogManager.getLogger(FirstRealm.class);
   @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
       //1. 从 PrincipalCollection 中来获取登录用户的信息
        Object principal = principals.getPrimaryPrincipal();

       //2. 利用登录的用户的信息来获取当前用户的角色或权限(可能需要查询数据库)
        Set<String> roles = new HashSet<>();
        roles.add("user");
       if("admin".equals(principal)){
            roles.add("abc");
        }

       //3. 创建 SimpleAuthorizationInfo, 并设置其 roles 属性.
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

       //4. 返回 SimpleAuthorizationInfo 对象.
       return info;
    }

   @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        logger.debug("[FirstRealm] doGetAuthenticationInfo");
       //1. 把 AuthenticationToken 转换为 UsernamePasswordToken
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

       //2. 从 UsernamePasswordToken 中来获取 username
        String username = upToken.getUsername();

       //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
        System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");

       //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
       if("unknown".equals(username)){
            throw new UnknownAccountException("用户不存在!");
        }
       //5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.
       if("monster".equals(username)){
            throw new LockedAccountException("用户被锁定");
        }

       //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
       //以下信息是从数据库中获取的.
       //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
        Object principal = username;
       //2). credentials: 密码.
        Object credentials =null;// "22935a8ea24e04937cfbd7c00fe6cccc864cc54e9b0c2cba10dd43a7aa7aabec";

       if("admin".equals(username)){
            credentials = "8b64db1b8cb9f9c2b2ae41c65b7f2c4b1456f68dd1235b8527234fae5e40bce5";
        }else if("user".equals(username)){
            credentials = "73d3b6cfc9d611fcd7744a97ed693d1c7b8b64c23587bcc6a4151ccae4bbea5a";
        }

       //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
        String realmName = getName();

       //4). 盐值.
        ByteSource credentialsSalt = ByteSource.Util.bytes(username);

        SimpleAuthenticationInfo info;// = new SimpleAuthenticationInfo(principal, credentials, realmName);
        info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
       return info;
    }


    public static void main(String[] args) {
        String hashAlgorithmName = "SHA-256";
        Object credentials = "123456";
        Object salt = ByteSource.Util.bytes("admin");
       int hashIterations = 1024;

        Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        System.out.println(result.toString());
    }}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 最简单的基于jquery的ajax请求教程

    上一次文章提到ajax异步请求的简单教程。那是基于原生JavaScript实现的。本次是基于jquery的ajax异步请求教程。

    世纪访客
  • Thymeleaf的使用前言:一、thymeleaf简介:二、thymeleaf标准方言:三、thymeleaf与springboot集成案例:总结:

    最近听说thymeleaf好像也挺流行的,还说是spring官方推荐使用,那thymeleaf究竟是什么呢?spring为什么推荐用它呢?怎么用呢?本文将为你揭...

    贪挽懒月
  • JavaScript中的沙箱机制探秘[二]:iFrame沙箱实现方案详解

    在上一篇文中,我们接触了JavaScript中的sandbox的概念,并且就现阶段的一些实现思路做了总结,包括YUI的闭包、iframe的sandbox以及No...

    星回
  • JavaScript中的沙箱机制探秘

    最近有需求要研究下开放给用户的自动化工具,于是就顺便整理了下沙箱的相关问题。Sandbox,中文称沙箱或者沙盘,在计算机安全中是个经常出现的名词。Sandbox...

    星回
  • 这一次,真正掌握composercomposer是现代PHP的基石初识composercomposer包管理规范

    现代高级编程语言,依赖管理工具是必不可少的。Java有Maven,Python有pip,Nodejs有npm, 而在composer出现之前,PHP只有被广为诟...

    章鱼喵
  • 用自动化测试工具selenium来揭露骗局的真相selenium进行页面滚动关闭chrome浏览器自动加载图片使用headless模式运行chrome删除页面上元素爬取结果分析源码

    前几天写了用爬虫来揭露约稿骗局的真相,但实际上对于动态加载的数据来说,用程序爬取比较困难,在这种情况下,可以使用selenium来模拟浏览器行为,达到同样目的。

    大神带我来搬砖
  • js重修课[六]:客户端JavaScript一些琐事

    星回
  • Javascript的内存泄漏分析

         作为程序员(更高大尚的称谓:研软件研发)的我们,无论是用Javascript,还是.net, java语言,肯定都遇到过内存泄漏的问题。只不过他们都有...

    sam dragon
  • 百度熊掌号折腾手记

    熊掌号出来有一段时间了,西枫里博客早早的就申请好了熊掌号。久久没有启用,放置了一段时间后,第一次启用熊掌号,发现博客程序中对缩略图定义的尺寸不符合要求,另外考虑...

    世纪访客
  • vue.js动画中的js钩子函数

    在transition中还可以通过设置javascript钩子函数,实现自定义动画效果。

    章鱼喵

扫码关注云+社区

领取腾讯云代金券