shiro原理

一、功能

  • 先上图
    shiro原理_第1张图片

shiro 四个主要的功能

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份
  • Authorization:授权,即权限验证,判断某个已经认证过的用户是否拥有某些权限访问某些资源,一般授权会有角色授权和权限授权
  • SessionManager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;web 环境中作用是和 HttpSession 是一样的
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

shiro 其它功能

  • Web Support:Web支持,可以非常容易的集成到Web环境
  • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率
  • Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去
  • Testing:提供测试支持
  • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

二、shiro 架构

  • 先上图
    shiro原理_第2张图片

主要组件

  • Subject:主体,相当与是请求过来的"用户"
  • SecurityManager: 是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行拦截并控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理
  • Realm:一般我们都需要去实现自己的 Realm ,可以有1个或多个 Realm,即当我们进行登录认证时所获取的安全数据来源(帐号/密码)
  • Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的

SecurityManager功能

  • Authenticator:认证器,负责主体认证的,即确定用户是否登录成功,我们可以使用  Shiro 提供的方法来认证,有可以自定义去实现,自己判断什么时候算是用户登录成功
  • Authrizer:授权器,即权限授权,给 Subject 分配权限,以此很好的控制用户可访问的资源
  • SessionManager:为了可以在不同的环境下使用 session 功能,shiro 实现了自己的 sessionManager ,可以用在非 web 环境下和分布式环境下使用
  • SessionDAO:对 session 的 CURD 操作
  • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的

Subject 认证主体

Subject 认证主体包含两个信息

  • Principals:身份,即用户名
  • Credentials:凭证,即密码

两个配置类ShiroConfig和ShiroAuthRealm

  • ShiroConfig
package admin.config;

import admin.filter.shiro.ShiroAuthFilter;
import admin.filter.shiro.ShiroAuthRealm;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Shiro配置
 * @Description :
 **/
@Configuration
public class ShiroConfig {

    /**
     * 配置安全管理器
     *
     * @return org.apache.shiro.mgt.SecurityManager
     * @Author : Huang Yun Song
     * @Date: 2020/7/16 9:18
     * @Param [oAuth2Realm]
     * @Description :
     **/
    @Bean("securityManager")
    public SecurityManager securityManager(ShiroAuthRealm oAuth2Realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //关联Realm
        securityManager.setRealm(oAuth2Realm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }

    /**
     * 配置过滤器
     *
     * @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
     * @Param [securityManager]
     * @Description :
     **/
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        //oauth过滤
        //添加Shiro拦截器
         /**
         * Shiro 内置过滤器,可以实现权限相关的拦截器
         *     anon:无需认证(登录)可以直接访问
         *     authc:必须认证才能访问
         *     user:如果使用rememberMe的功能才可以访问
         *     perms:该资源得到资源权限才可以访问
         *     role:该资源必须得到角色权限才可以访问
         */
        Map<String, Filter> filters = new HashMap<>();
        filters.put("adminAuth", new ShiroAuthFilter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
       //        filterMap.put("/favicon.ico*", "anon");  //图标
//        filterMap.put("/ueditor/**", "anon"); //百度编辑器
//        filterMap.put("/static/**", "anon");  //静态资源
//        filterMap.put("/error", "anon");  //错误
//        filterMap.put("/common/**", "anon");  //通用控制器
//        filterMap.put("/robots.txt", "anon");  //SEO设置
//        filterMap.put("/healthy.html", "anon");  //健康检查
//        filterMap.put("/mng/login.html", "anon");  //登录页面
//        filterMap.put("/mng/login", "anon");  //登录操作
//        filterMap.put("/mng/checkSmsLogin", "anon");  //短信验证
//        filterMap.put("/mng/getCaptcha", "anon");  //获取图形验证码
//        filterMap.put("/mng/forgetPassword.html", "anon");  //忘记密码页面渲染
//        filterMap.put("/mng/forgetPassword", "anon");  //忘记密码修改密码
//        filterMap.put("/mng/sendSmsCode", "anon");  //发送短信验证码
//        filterMap.put("/mng/sendLoginSmsCode", "anon");  //发送短信验证码

        filterMap.put("/**", "adminAuth");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}

  • ShiroAuthRealm
    登录认证用redis缓存用户信息,JWT进行加密加盐,shiro也有自带的加密工具

package admin.filter.shiro;

import basic.enums.CacheSceneEnum;
import basic.util.cache.ICache;
import common.server.sys.admin.entity.CurrAdmin;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 授权领域
 *
 * @Description :
 *         1、检查提交的进行认证的令牌信息
 *         2、根据令牌信息从数据源(通常为数据库)中获取用户信息
 *         3、对用户信息进行匹配验证。
 *         4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。
 *         5、验证失败则抛出AuthenticationException异常信息。
 **/
@Component
public class ShiroAuthRealm extends AuthorizingRealm {

    protected Logger log = LoggerFactory.getLogger(ShiroAuthRealm.class);

    @Autowired
    ICache cache;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof ShiroAuthToken;
    }

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        CurrAdmin currAdmin = (CurrAdmin) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(currAdmin.getPermissions());  //用户权限列表
        return info;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        String token = (String) authenticationToken.getPrincipal();

        //根据token,查询adminId
        Long adminId = cache.get(CacheSceneEnum.ADMIN_LOGIN_TOKEN.keyProfix + token, Long.class);
        if (adminId == null) {
            log.info("登录认证时,adminId为空");
            throw new AuthenticationException("登录失效,请重新登录");
        }

        //根据adminId,查用户缓存信息
        CurrAdmin currAdmin = cache.get(CacheSceneEnum.ADMIN_LOGIN_INFO.keyProfix + adminId, CurrAdmin.class);
        if (currAdmin == null) {
            log.info("登录认证时,currAdmin为空");
            throw new AuthenticationException("登录失效,请重新登录");
        }

        if (!token.equals(currAdmin.getLoginToken())) {
            log.info("登录认证时,token和当前登录管理员token不一致!");
            //暂时先注释
            //throw new MyShiroException(ErrorCodeEnum.FORCED_OFFLINE_ERROR);
        }

        //刷新登录状态
        if (System.currentTimeMillis() - currAdmin.getLastSetTime() > 120000) {  //3分钟更新一次缓存时间
            currAdmin.setLastSetTime(System.currentTimeMillis());
            cache.set(CacheSceneEnum.ADMIN_LOGIN_TOKEN.keyProfix + token, adminId,
                    CacheSceneEnum.ADMIN_LOGIN_TOKEN.expireTime);
            cache.set(CacheSceneEnum.ADMIN_LOGIN_INFO.keyProfix + adminId, currAdmin,
                    CacheSceneEnum.ADMIN_LOGIN_INFO.expireTime);
        }

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(currAdmin, token, getName());
        return info;
    }
}

你可能感兴趣的:(安全,shiro)