zoukankan      html  css  js  c++  java
  • 关于shiro

      shiro作为一个功能强大而且可靠的安全框架拥有很多Api,结合spring可以很方便的实现有关shiro的使用。

    这是关于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存取认证、授权相关数据。

    下面时一些shiro提供的方法 可继承重写

    Subject

    Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

     SecurityManager 

    SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

    SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

     Authenticator

    Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

    Authorizer

    Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

     realm

    Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

    注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

     sessionManager

    sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

     SessionDAO

    SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

    CacheManager

    CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

    Cryptography

    Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

    1.shiro的必要依赖

    这里使用maven 所以我贴出pom的依赖包 ,如果需要手动导入的话可以下载相应的版本的jar包即可

    <properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <shiro.version>1.4.0</shiro.version>
    </properties>
    <dependency>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                    <version>${shiro.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-ehcache</artifactId>
                    <version>${shiro.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-web</artifactId>
                    <version>${shiro.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-spring</artifactId>
                    <version>${shiro.version}</version>
                </dependency>
    
    <!--这里spring的就不贴了根据需要导入吧  全部导入也ok没有冲突的-->

    shiro在spring的applicationContext.xml的配置

    <!-- shiroFilter 的webfilter -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!-- 管理器 必须配置 -->
            <property name="securityManager" ref="securityManager"></property>
            <!-- 拦截 跳转到的地址 通过地址去认证 -->
            <property name="loginUrl" value="/html/user_login.html"></property>
            <!-- 认证通过 可在登陆处理中跳转 也可配置如下 -->
            <property name="successUrl" value="/jsp/index.jsp"></property>
            <!-- 未认证跳转的连接 -->
            <property name="unauthorizedUrl" value="/error.html"></property>
            <property name="filterChainDefinitions">
                <value>
                    <!-- 静态资源设置匿名访问 -->
                    /js/**=anon
                    /css/**=anon
                    /html/**=anon
                    /jsp/**=anon
                    /user/logOut=logout
                
                </value>
            </property>
        </bean>
    
        <!-- securityManager安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="myRealm"></property>
            <!-- cacheManager 还可以在Realm中注册 反正注册到这里也是securityManager给realm调用 -->
            <property name="cacheManager" ref="cacheManager"></property>
            <!-- 注入 rememberMe  cookie管理器 -->
            <property name="rememberMeManager" ref="rememberMeManager"></property>
        </bean>
        
        
        
        <!-- 自定义Realm 注册Realm -->
        <bean id="myRealm" class="com.dabai.realm.MyRealm">
            <property name="cacheManager" ref="cacheManager"></property>
            <!-- 将凭证匹配器设置到realm中 realm按照凭证匹配器要求散列 -->
            <property name="credentialsMatcher" ref="credentialsMatcher"></property>
            <!--开启授权缓存 -->
            <property name="authorizationCachingEnabled" value="true" />
            <!--缓存开启 -->
            <property name="cachingEnabled" value="true" />
            <!--身份认证 -->
            <property name="authenticationCachingEnabled" value="true" />
            <property name="authenticationCacheName" value="my" />
            <!--权限认证 -->
            <property name="authorizationCacheName" value="my" />
        </bean>
        
        
        <!-- 凭证匹配器 -->
        <bean id="credentialsMatcher"
            class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!-- md5散列 -->
            <property name="hashAlgorithmName" value="md5"></property>
            <!-- 一次 -->
            <property name="hashIterations" value="2"></property>
            <property name="storedCredentialsHexEncoded" value="true"></property>
        </bean>
    
    
        <!-- -->
        <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <constructor-arg name="name" value="jeesite.session.id" />
        </bean>
    
    
    
        <!-- 会话管理器 -->
        <bean id="sessionManager"
            class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session失效时间 毫秒 -->
            <property name="globalSessionTimeout" value="60000"></property>
            <!-- 删除失效session -->
            <property name="deleteInvalidSessions" value="true"></property>
    
        </bean>
        <bean id="ehCacheManager"
            class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
            <property name="configLocation" value="classpath:shiroEhcache.xml"></property>
        </bean>
        
        
        <!-- 缓存管理器 -->
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManager" ref="ehCacheManager"></property>
        </bean>
        
        
        <!-- rememberMeManager 管理器 写cookie 取出cookie生成用户信息 -->
        <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <!-- 自定义cookie -->
            <property name="cookie" ref="rememberMeCookie"></property>
             
        </bean>
        
        <!-- 指定cookie -->
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <constructor-arg value="rememberMe"></constructor-arg>
            <property name="httpOnly" value="true"></property>
            <property name="maxAge" value="604800"></property>
        </bean>
        
        
        <!-- -->
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
        <!-- shiro end -->

    上述有些东西是不需要的  如 ehcache的缓存 ,如果只是用于学习没有大量的数据的话可以不必配置,id="rememberMeCookie"  这个bean也可以不必使用 ,配置了如果开启的话,shiro会维护一个session用于校验登陆用户的身份,通过的话而且又在session的生命周期内时用户能够不登陆而以 shiro提供的 user  身份访问shirofilter 中设置拦截规则为 user的url 

    <bean id="cacheManager"这个也是不必要的  视需求吧 ,初步学习可以了解即可。其他的参考注释。
    2.shiro通过filter来进行拦截所以使用的话需要在web.xml中配置filter
    <!-- spring 整合安全框架 
        由spring管理 配置原则上由spring配置  DelegatingFilterProxy作用时通知spring将所有的filter交给ShiroFilter
    -->
      <filter>
          <filter-name>shiroFilter</filter-name>
          <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
          <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>

    记得 shiro的filter targetFilterLifecycle这个属性要配置上去。。。

    这里说一下realm :

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

    功能
    Realm能做的工作主要有以下几个方面:

    身份验证(getAuthenticationInfo 方法)验证账户和密码,并返回相关信息

    权限获取(getAuthorizationInfo 方法) 获取指定身份的权限,并返回相关信息

    令牌支持(supports方法)判断该令牌(Token)是否被支持

    令牌有很多种类型,例如:HostAuthenticationToken(主机验证令牌),UsernamePasswordToken(账户密码验证令牌)

     使用中可以自定义realm也可以用shiro提供的,通常我们会自定义realm

    shiro在登陆逻辑处进行校验权限 realm中提供 一个父类 AuthorizingRealm,继承该类需要覆写两个方法 验证和授权 具体如下

    package com.dabai.realm;
    
    import java.util.List;
    
    import javax.annotation.Resource;
    
    import org.apache.commons.lang3.StringUtils;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    import com.dabai.pojo.User;
    import com.dabai.service.UserService;
    
    /**
     * @author dabai:
     * 
     *         类说明 shiro realm
     */
    public class MyRealm extends AuthorizingRealm {
    
        @Resource(name = "userService")
        private UserService userService;
    
        @Override
        public void setName(String name) {
            super.setName(name);
        }
    
        @Override
        public String getName() {
            return "myRealm";
        }
    
        // 授权 认证通过后才会执行
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            /*
             * 从principals获取主身份信息 将getPrimaryPrincipal方法返回值转为真实身份类型
             */
            if (principals == null || principals.getPrimaryPrincipal() == null) {
                return null;
            }
            User loginUser = (User) SecurityUtils.getSubject().getPrincipal();
            // 获取权限 从数据库中取 根据用户角色查询其privilege
            List<String> privileges = userService.findPrivilegesById(loginUser);
            // realm中权限添加
            for (String privilege : privileges) {
                info.addStringPermission(privilege);
            }
            return info;
        }
    
        // 认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            // 获取用户名
            UsernamePasswordToken passwordToken = (UsernamePasswordToken) token;
            String username = passwordToken.getUsername();
            if (username == null) {
                return null;
            }
            User checkUser = userService.checkRealmUserName(username);
            if (checkUser == null) {
                System.out.println("shiro 认证  当前登陆用户不存在");
                throw new RuntimeException("shiro authentication  failed");
            }
            String password = checkUser.getPassword();
            // 正确的验证信息 Token 
            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(checkUser, password,ByteSource.Util.bytes(username), this.getName());
             
            return authcInfo;
        }
    }

    realm中在登陆方法中被调用 参考如下的login方法

    该方法在UserController中
    public String login(User user, Model model ) {
            // 执行登陆逻辑 shiro的登陆逻辑
             
            if (user == null || StringUtils.isBlank(user.getUsername()))
                return "user_login";
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
            try {
                 
                subject.login(token);
            } catch (Exception e) {
                errorInfo = "wrong username or password";
                model.addAttribute("errorInfo", errorInfo);
                
                return "user_login";
            }
            User loginU = userService.checkRealmUserName(user.getUsername());
            Session session = subject.getSession(true);
            session.setAttribute("loginUser", loginU);
            return "redirect:index";
        }
     subject.login(token);

    上面那行会在shiro中调用realm的方法  还需要注意的是 授权方法只会在需要校验权限的时候被调用。

    全部代码在

    git 上

  • 相关阅读:
    洛谷P3455
    开发人员的奋斗目标
    js判读周末以及节假日
    c#中集成Swagger
    Combo Select – jQuery可搜索下拉框插件
    接口对接 调用与处理方式
    问题集锦
    sql server 自定义函数的使用
    Api接口服务的设计和安全解决方案
    使用Jquery Ajax请求 下载压缩文件
  • 原文地址:https://www.cnblogs.com/notably/p/10676183.html
Copyright © 2011-2022 走看看