美文网首页
springboot+shiro整合

springboot+shiro整合

作者: _麻辣香锅不要辣 | 来源:发表于2019-03-27 09:38 被阅读0次

    springboot+shiro

    最近在看shiro和jwt,遇到了好多坑,现在总结一下

    不了解shiro的话,可以先看下这个:shiro基础教程

    以下内容参考(写的很简洁明了):教你 Shiro 整合 SpringBoot,避开各种坑

    导包

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

    springboot整合shiro

    shiro配置类

    
    @Configuration
    
    public class ShiroConfig {
    
        @Bean
    
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    
           // RestShiroFilterFactoryBean shiroFilterFactoryBean = new RestShiroFilterFactoryBean();
    
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean ()
            // 必须设置 SecurityManager
    
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
    
            shiroFilterFactoryBean.setLoginUrl("/notLogin");
    
            // 设置无权限时跳转的 url;
    
            shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
    
            // 设置拦截器
    
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    
            filterChainDefinitionMap.put("/logout","anon");
    
            filterChainDefinitionMap.put("/login","anon");
    
            filterChainDefinitionMap.put("/user/**","roles[user]");
    
            //其余接口一律拦截
    
            //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
    
            filterChainDefinitionMap.put("/**", "authc");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    
            System.out.println("Shiro拦截器工厂类注入成功");
    
            return shiroFilterFactoryBean;
    
        }
    
        /**
    
        * 注入 securityManager
    
        */
    
        @Bean
    
        public SecurityManager securityManager() {
    
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    
            // 设置realm.
    
            securityManager.setRealm();
    
            //securityManager.setRealms(Arrays.asList(customRealm(hashedCredentialsMatcher()),jwtRealm()));
    
            return securityManager;
    
        }
    
        /**
    
        * 自定义身份认证 realm;
    
        * <p>
    
        * 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm,
    
        * 否则会影响 CustomRealm类 中其他类的依赖注入
    
        */
    
        @Bean
    
        public CustomRealm customRealm() {
    
            CustomRealm customRealm=new CustomRealm();
    
            return  customRealm;
    
        }
    
        /**
    
        * 禁用session, 不保存用户登录状态。保证每次请求都重新认证。
    
        * 需要注意的是,如果用户代码里调用Subject.getSession()还是可以用session,如果要完全禁用,要配合下面的noSessionCreation的Filter来实现
    
        */
    
        @Bean
    
        protected SessionStorageEvaluator sessionStorageEvaluator(){
    
            DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
    
            sessionStorageEvaluator.setSessionStorageEnabled(false);
    
            return sessionStorageEvaluator;
    
        }
    
    }
    
    

    注意:里面的 SecurityManager 类导入的应该是 import org.apache.shiro.mgt.SecurityManager; 但是,如果你是复制代码过来的话,会默认导入 java.lang.SecurityManager 这里也稍稍有点坑,其他的类的话,也是都属于 shiro 包里面的类

    shirFilter 方法中主要是设置了一些重要的跳转 url,比如未登陆时,无权限时的跳转;以及设置了各类 url 的权限拦截,比如 /user 开始的 url 需要 user 权限,/admin 开始的 url 需要 admin 权限等

    (这里的跳转会涉及到跨域的问题,因为rediret重定向在跨域中会有问题)

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

    自定义realm类

    
    public class CustomRealm extends AuthorizingRealm {
    
        @Autowired
    
        private UserMapper userMapper;
    
        /**
    
        *  当token类型是UsernamePasswordToken时才调用这个realm(在之后整合jwt的时候还有一个jwttoken,所以这里要加这个)
    
        */
    
        @Override
    
        public boolean supports(AuthenticationToken token) {
    
            return token instanceof UsernamePasswordToken;
    
        }
    
        /**
    
        * 身份认证
    
        * Shiro中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。
    
        * @param authenticationToken 用户身份信息 token
    
        * @return 返回封装了用户信息的 AuthenticationInfo 实例
    
        */
    
        @Override
    
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    
            // 从数据库获取对应用户名密码的用户
    
                User user = userMapper.selectbyUsername(token.getUsername());
    
                String password="";
    
                if(user!=null)
    
                {password=user.getPassword();}
    
            return new SimpleAuthenticationInfo(token.getPrincipal(),
    
            password,
    
              getName());
    
        }
    
        /**
    
        * 权限认证
    
        * @param principalCollection
    
        * @return
    
        */
    
        @Override
    
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
            String username = (String) SecurityUtils.getSubject().getPrincipal();
    
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    
            //获得该用户角色
    
            String role=userMapper.selectbyUsername(username).getRole();
    
            //String role = userMapper.getRole(username);
    
            Set<String> set = new HashSet<>();
    
            //需要将 role 封装到 Set 作为 info.setRoles() 的参数
    
            set.add(role);
    
            //设置该用户拥有的角色
    
            info.setRoles(set);
    
            return info;
    
        }
    
    }
    
    

    重写的两个方法分别是实现身份认证以及权限认证,shiro 中有个作登陆操作的 Subject.login() 方法,当我们把封装了用户名,密码的 token 作为参数传入,便会跑进这两个方法里面(不一定两个方法都会进入)

    其中 doGetAuthorizationInfo 方法只有在需要权限认证时才会进去,比如前面配置类中配置了 filterChainDefinitionMap.put("/user/**", "roles[user]"); 的管理员角色,这时进入 /user 时就会进入 doGetAuthorizationInfo 方法来检查权限;而 doGetAuthenticationInfo 方法则是需要身份认证时(比如前面的 Subject.login() 方法)才会进入

    再说下 UsernamePasswordToken 类,我们可以从该对象拿到登陆时的用户名和密码(登陆时会使用 new UsernamePasswordToken(username, password);)

    loginController

    
    @RequestMapping(value = "/login")
    
        public boolean login(@RequestBody User user ) {
    
        String username=user.getUsername();
    
        String password=user.getPassword();
    
            // 从SecurityUtils里边创建一个 subject
    
            Subject subject = SecurityUtils.getSubject();
    
            // 在认证提交前准备 token(令牌)
    
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    
            // 执行认证登陆
    
            //在登录时会调用CustomRealm中的doGetAuthenticationInfo方法,返回一个SimpleAuthenticationInfo
    
            //这个是在内部根据usernamepasswordtoken判断用户名密码是否正确,如果正确的话不会抛出异常,否则抛出
    
            //异常,在loginController中将异常捕获,并返回false
    
            try {
    
                subject.login(token);
    
                return true;
    
            }catch (Exception e)
    
            {
    
                return false;
    
            }
    
        }
    
    

    相关文章

      网友评论

          本文标题:springboot+shiro整合

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