shiro学习

一、什么是Shiro

Apache Shiro 是一个强大且易用的 Java 安全框架 , 执行 身份验证、授权、密码和会话管理 。使用 Shiro 的易于理解的
API, 您可以快速、轻松地获得任何应用程序 , 从最小的移动应用程序到最大的网络和企业应用程序。
Apache Shiro 的首要目标是易于使用和理解。安全有时候是很复杂的,甚至是痛苦的,但它没有必要这样。框架
应该尽可能掩盖复杂的地方,露出一个干净而直观的 API ,来简化开发人员在使他们的应用程序安全上的努力。以
下是你可以用 Apache Shiro 所做的事情:
  • 验证用户来核实他们的身份
  • 对用户执行访问控制,如:①判断用户是否被分配了一个确定的安全角色 ② 判断用户是否被允许做某事
  • 在任何环境下使用 Session API,即使没有 Web EJB 容器。
  • 在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。
  • 聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户视图
  • 启用单点登录(SSO)功能。
  • 为没有关联到登录的用户启用"Remember Me"服务

二、Shiro的功能模块

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。 Shiro 可以帮助
我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。这不就是我们想要的嘛,而且 Shiro API 也是非
常简单;其基本功能点如下图所示:
  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份。
  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情。
  • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话 中;会话可以是普通JavaSE环境的,也可以是如Web环境的。
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
  • Web SupportShiro web 支持的 API 能够轻松地帮助保护 Web 应用程序。
  • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率。
  • ConcurrencyApache Shiro 利用它的并发特性来支持多线程应用程序。
  • Testing:测试支持的存在来帮助你编写单元测试和集成测试,并确保你的能够如预期的一样安全。
  • "Run As":一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本很有用。
  • "Remember Me":记住我。

三、Shiro的内部结构

Subject :主体,可以看到主体可以是任何可以与应用交互的 用户
SecurityManager :相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher ;是 Shiro 的心
脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject 、且负责进行认证和授权、及会
话、缓存的管理。
Authenticator :认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实
现;其需要认证策略( Authentication Strategy ),即什么情况下算用户认证通过了;
Authrizer :授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的
哪些功能;
Realm :可以有 1 个或多个 Realm ,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可
以是 LDAP 实现,或者内存实现等等;由用户提供;注意: Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;
所以我们一般在应用中都需要实现自己的 Realm
SessionManager :如果写过 Servlet 就应该知道 Session 的概念, Session 呢需要有人去管理它的生命周期,这个
组件就是 SessionManager ;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、 EJB 等环境;
所有呢, Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;
SessionDAO DAO 大家都用过,数据访问对象,用于会话的 CRUD ,比如我们想把 Session 保存到数据库,那么可
以实现自己的 SessionDAO ,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的
Memcached SessionDAO ;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
CacheManager :缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到
缓存中后可以提高访问的性能
Cryptography :密码模块, Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

四、应用程序使用Shiro

也就是说对于我们而言,最简单的一个 Shiro 应用:
1 、应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager
2 、我们需要给 Shiro SecurityManager 注入 Realm ,从而让 SecurityManager 能得到合法的用户及其权限进行判
断。
从以上也可以看出, Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。
 

五、应用实战

1、引入依赖 父工程pom.xml

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-core</artifactId>
     <version>1.3.2</version>
</dependency

2、ihrm_system模块登录方法

@RequestMapping(value="/login",method = RequestMethod.POST)
    public Result login(@RequestBody Map<String,String> loginMap) {
        String mobile = loginMap.get("mobile");
        String password = loginMap.get("password");
        try {
            //1.构造登录令牌 UsernamePasswordToken
            //加密密码
            password = new Md5Hash(password,mobile,3).toString();  //1.密码,盐,加密次数
            UsernamePasswordToken upToken = new UsernamePasswordToken(mobile,password);
            //2.获取subject
            Subject subject = SecurityUtils.getSubject();
            //3.调用login方法,进入realm完成认证
            subject.login(upToken);
            //4.获取sessionId
            String sessionId = (String)subject.getSession().getId();
            //5.构造返回结果
            return new Result(ResultCode.SUCCESS,sessionId);
        }catch (Exception e) {
            return new Result(ResultCode.MOBILEORPASSWORDERROR);
        }

3、自定义realm域

Realm 域: Shiro Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么
它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行
验证用户是否能进行操作;可以把 Realm 看成 DataSource ,即安全数据源
 
注:主要考虑到授权、认证的方法
 
public class CustomRealm extends AuthorizingRealm {
    @Override
    public void setName(String name) {
        super.setName("customRealm");
   }
    @Autowired
    private UserService userService;
    /**
     * 构造授权方法
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection
principalCollection) {//1.获取认证的用户数据
        User user = (User)principalCollection.getPrimaryPrincipal();
        //2.构造认证数据
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<Role> roles = user.getRoles();
        for (Role role : roles) {
            //添加角色信息
            info.addRole(role.getName());
            for (Permission permission:role.getPermissions()) {
                //添加权限信息
                info.addStringPermission(permission.getCode());
           }
       }
        return info;
   }
    /**
     * 认证方法
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
authenticationToken) throws AuthenticationException {
        //1.获取登录的upToken
        UsernamePasswordToken upToken = (UsernamePasswordToken)authenticationToken;
        //2.获取输入的用户名密码
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());
        //3.数据库查询用户
        User user = userService.findByName(username);
        //4.用户存在并且密码匹配存储用户数据
        if(user != null && user.getPassword().equals(password)) {
            return new
SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
       }else {
            //返回null会抛出异常,表明用户不存在或密码不匹配
            return null;
       }
   }
}
@Configuration
public class ShiroConfiguration {
    //配置自定义的Realm
    @Bean
    public CustomRealm getRealm() {
        return new CustomRealm();
   }

    //配置安全管理器
    @Bean
    public SecurityManager securityManager(CustomRealm realm) {
        //使用默认的安全管理器
        DefaultWebSecurityManager securityManager = new
DefaultWebSecurityManager(realm);
        //将自定义的realm交给安全管理器统一调度管理
        securityManager.setRealm(realm);
        return securityManager;
   }
    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        //1.创建shiro过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        filterFactory.setSecurityManager(securityManager);
        //3.通用配置(配置登录页面,登录成功页面,验证未成功页面)
        filterFactory.setLoginUrl("/autherror?code=1"); //设置登录页面
        filterFactory.setUnauthorizedUrl("/autherror?code=2"); //授权失败跳转页面
        //4.配置过滤器集合
        /**
         * key :访问连接
         *     支持通配符的形式
         * value:过滤器类型
         *     shiro常用过滤器
         *         anno   :匿名访问(表明此链接所有人可以访问)
         *         authc   :认证后访问(表明此链接需登录认证成功之后可以访问)
         */
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        // 配置不会被拦截的链接 顺序判断
        filterMap.put("/user/home", "anon");
        filterMap.put("/user/**", "authc");
        //5.设置过滤器
        filterFactory.setFilterChainDefinitionMap(filterMap);
        return filterFactory;
   }
    //配置shiro注解支持
    @Bean
    public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new
AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
   }
}

四、Shiro的配置

SecurityManager Shiro 架构的心脏,用于协调内部的多个组件完成全部认证授权的过程。例如通过调用 realm
完成认证与登录。使用基于 springboot 的配置方式完成 SecurityManager Realm 的装配
package com.ihrm.system;

import com.ihrm.common.shiro.realm.IhrmRealm;
import com.ihrm.common.shiro.session.CustomSessionManager;
import com.ihrm.system.shiro.realm.UserRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfiguration {

    //1.创建realm
    @Bean
    public IhrmRealm getRealm() {
        return new UserRealm();
    }

    //2.创建安全管理器
    @Bean
    public SecurityManager getSecurityManager(IhrmRealm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);

        //将自定义的会话管理器注册到安全管理器中
        securityManager.setSessionManager(sessionManager());
        //将自定义的redis缓存管理器注册到安全管理器中
        securityManager.setCacheManager(cacheManager());

        return securityManager;
    }

    //3.配置shiro的过滤器工厂

    /**
     * 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
     *
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        //1.创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        filterFactory.setSecurityManager(securityManager);
        //3.通用配置(跳转登录页面,未授权跳转的页面)
        filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址
        filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url
        //4.设置过滤器集合
        Map<String,String> filterMap = new LinkedHashMap<>();
        //anon -- 匿名访问
        filterMap.put("/sys/login","anon");
        filterMap.put("/sys/faceLogin/**","anon");
        filterMap.put("/autherror","anon");
        //注册
        //authc -- 认证之后访问(登录)
        filterMap.put("/**","authc");
        //perms -- 具有某中权限 (使用注解配置授权)
        filterFactory.setFilterChainDefinitionMap(filterMap);

        return filterFactory;
    }


    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;

    /**
     * 1.redis的控制器,操作redis
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        return redisManager;
    }

    /**
     * 2.sessionDao
     */
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }

    /**
     * 3.会话管理器
     */
    public DefaultWebSessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        //禁用cookie
        //sessionManager.setSessionIdCookieEnabled(false);
        //禁用url重写   url;jsessionid=id
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * 4.缓存管理器
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }




    //开启对shior注解的支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

5、shiro中的过滤器

注意: anon, authc, authcBasic, user 是第一组认证过滤器, perms, port, rest, roles, ssl 是第二组授权过滤
器,要通过授权过滤器,就先要完成登陆认证操作(即先要完成认证才能前去寻找授权 ) 才能走第二组授权器
(例如访问需要 roles 权限的 url ,如果还没有登陆的话,会直接跳转到
shiroFilterFactoryBean.setLoginUrl(); 设置的 url
 

6、授权

授权:即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情
shiro 支持基于过滤器的授权方式也支持注解的授权方式

6.1、基于配置的授权

shiro中可以使用过滤器的方式配置目标地址的请求权限

//配置请求连接过滤器配置
        //匿名访问(所有人员可以使用)
        filterMap.put("/user/home", "anon");
        //具有指定权限访问
        filterMap.put("/user/find", "perms[user-find]");
        //认证之后访问(登录之后可以访问)
        filterMap.put("/user/**", "authc");
        //具有指定角色可以访问
        filterMap.put("/user/**", "roles[系统管理员]");
基于配置的方式进行授权,一旦操作用户不具备操作权限,目标地址不会被执行。会跳转到指定的 url 连接地
址。所以需要在连接地址中更加友好的处理未授权的信息提示
 

6.2、基于注解的授权

1 RequiresPermissions
配置到方法上,表明执行此方法必须具有指定的权限
//查询
    @RequiresPermissions(value = "user-find")
    public String find() {
        return "查询用户成功";
   }
2 RequiresRoles
配置到方法上,表明执行此方法必须具有指定的角色
  //查询
    @RequiresRoles(value = "系统管理员")
    public String find() {
        return "查询用户成功";
   }
基于注解的配置方式进行授权,一旦操作用户不具备操作权限,目标方法不会被执行,而且会抛出
AuthorizationException 异常。所以需要做好统一异常处理完成未授权处理
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值