SpringBoot与Shiro整合

作者: 椰子奶糖 | 来源:发表于2019-07-21 23:04 被阅读23次

    SpringBoot与Shiro整合

    Shiro基本概念

    • 先从官网盗一点概念下来:
    • Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
    • Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

    Apache Shiro 体系结构

    Apache Shiro 体系结构
    • 1、Authentication 认证 ---- 用户登录
    • 2、Authorization 授权 --- 用户具有哪些权限
    • 3、Cryptography 安全数据加密
    • 4、Session Management 会话管理
    • 5、Web Integration web系统集成
    • 6、Interations 集成其它应用,spring、缓存框架

    关于Shiro的Quickstart

    官方Quickstart:
    http://shiro.apache.org/10-minute-tutorial.html


    Springboot 整合Shiro

    • 本次实验基于Springboot的一个基本登录案例来测试Shiro的安全认证、授权功能
      • 要使用Shiro我们需要ShiroFilterFactoryBean
      • 而ShiroFilterFactoryBean需要设置DefaultWebSecurityManager管理器
      • 而DefaultWebSecurityManager需要设置UserRealm
      • 于是整个shiro的配置就变成这样:
     package zstu.edu.springboot.shiro;
    
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * shiro的配置类
     * Created by CHEN on 2019/7/20.
     */
    @Configuration
    public class ShiroConfig {
    
    
        /**
         * 创建ShiroFilterFactoryBean
         *
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
    
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    
    
            //添加shiro的内置过滤器
            /**
             * Shiro内置过滤器,可以实现权限相关的拦截器
             *      常用过滤器:
             *          anon:无需认证(登录)可以访问
             *          authc:必须认证才可以访问
             *          user:如果使用rememberMe的功能可以直接访问
             *          perms:该资源必须得到资源权限才可以访问
             *          role:该资源必须得到角色权限才可以访问
             */
            Map<String,String> filterMap = new LinkedHashMap<>();
    //        filterMap.put("/add","authc");
    //        filterMap.put("/update","authc");
    
            //可以这么写
            //这里应当注意顺序,上下换一下就算设置了anon还是会被拦截,
            //而且目前的顺序会先调用/testThymeleaf,然后再调用login,login还是调用了,只是因为权限是anon所以才没有彖传
            //testThymeleaf..........
            //login................
    
            filterMap.put("/testThymeleaf","anon");
            filterMap.put("/login","anon");
    
            //授权过滤器
            //当前授权拦截后shiro会自动跳转到未授权页面
            filterMap.put("/add","perms[user:add]");
    
    
            filterMap.put("/*","authc");
    
            //修改默认登陆界面
            shiroFilterFactoryBean.setLoginUrl("/tologin");
    
            //设置未授权提示页面
            shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    
    
            return shiroFilterFactoryBean;
    
        }
    
    
        /**
         * @Qualifier拿到容器中的UserRealm对象
         * 创建DefaultWebSecurityManager
         */
        @Bean(name="securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //关联一个Realm
            securityManager.setRealm(userRealm);
    
            return securityManager;
        }
    
    
        /**
         * 创建Realm
         *
         * @return
         */
        @Bean(name = "userRealm")
        public UserRealm getRealm() {
            return new UserRealm();
        }
    }
    
    
    • 这里需要注意的是我们需要把定义的组件加到容器中,这样我们才能找到这些组件,@Bean中的name属性也可以不用设置,直接用他的默认名即可
    • 而UserRealm也是一个自定义类,他需要继承AuthorizingRealm类
    package zstu.edu.springboot.shiro;
    
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    /**
     * 自定义Realm
     * Created by CHEN on 2019/7/20.
     */
    public class UserRealm  extends AuthorizingRealm {
    
        /**
         * 执行授权逻辑
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行授权逻辑");
    
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    
            //应当对接数据库,这里为了方便,直接写死
            info.addStringPermission("user:add");
    
            return info;
        }
    
    
        /**
         * 执行认证的逻辑
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行认证逻辑");
    
            //后续应当从数据库中获得,先写死
            String name = "123";
            String password = "123";
    
            //判断用户名
    
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    
            if (!token.getUsername().equals(name)){
                //用户名不存在
                return null;
                //shiro底层会抛出UnKnownAccountException
            }
    
            return new SimpleAuthenticationInfo("",password,"");
        }
    }
    
    

    分析

    • 首先,是认证Authentication,认证发生在用户登录,我们需要在controller中写出他的逻辑:
    • 步骤如下:
      • 1.获取Subject Subject subject = SecurityUtils.getSubject();
      • 2.封装用户数据 UsernamePasswordToken
      • 3.执行登录方法 subject.login(token);
    @RequestMapping("/login")
        public String login(String name ,String password,Model model) {
            System.out.println("name="+name);
            /**
             * 使用Shiro编写认证操作
             */
            //1.获取Subject
            Subject subject = SecurityUtils.getSubject();
    
            //封装用户数据
            UsernamePasswordToken token = new UsernamePasswordToken(name,password);
    
            //执行登录方法
            try {
                subject.login(token);
                //登录成功
    
                //跳转到test.html
                return "redirect:/testThymeleaf";
            }catch (UnknownAccountException e ){
                model.addAttribute("msg","用户名不存在");
                System.out.println("UnknownAccountException....................");
                return "login";
            }catch (IncorrectCredentialsException e ){
                model.addAttribute("msg","密码错误");
                System.out.println("IncorrectCredentialsException.......................");
                return "login";
            }
        }
    
    

    认证流程:

    认证流程

    授权流程:

    授权流程

    最后写一点认证拦截

        //这里应当注意顺序,上下换一下就算设置了anon还是会被拦截,
        //而且目前的顺序会先调用/testThymeleaf,然后再调用login,login还是调用了,只是因为权限是anon所以才没有彖传
        //testThymeleaf..........
        //login................
    
        filterMap.put("/testThymeleaf","anon");
        filterMap.put("/login","anon");
    
        filterMap.put("/*","authc");
    
        //所以个人认为避免全拦截的好,拦截的方统一目录下,在认证拦截的时候只需要拦截这个文件下所有文件的请求
    

    相关文章

      网友评论

        本文标题:SpringBoot与Shiro整合

        本文链接:https://www.haomeiwen.com/subject/wkrblctx.html