前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shiro集成应用 原

Shiro集成应用 原

作者头像
wuweixiang
发布2018-08-14 14:31:15
4990
发布2018-08-14 14:31:15
举报
文章被收录于专栏:吴伟祥吴伟祥
http://shiro.apache.org/assets/images/apache-shiro-logo.png
http://shiro.apache.org/assets/images/apache-shiro-logo.png

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

代码语言:javascript
复制
<!--添加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配置 

代码语言:javascript
复制
<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类

代码语言:javascript
复制
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());
    }}

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017/11/13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 〇、Apache Shiro官网
  • 一、什么是Shiro
  • 二、Shiro的架构介绍
  • 三、Shiro与Spring的集成
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档