关于shiro

北慕城南 提交于 2020-02-22 17:53:16

  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 上

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!