springboot整合shiro安全管理框架

简介


shiro是什么

       apache shiro 开源安全框架 , 一个更加健全的RBAC框架 , 干净利落处理身份认证 , 授权 , 企业会话管理 ,加密和缓存等功能 .

shiro和spring security

       在shiro之前安全管理框架还有spring security , 它依赖于spring , 更加复杂 , 学习曲线高 , 成本也高 . shiro比较独立 , 并且可以在分布式集群环境中使用 .

shiro结构体系

在这里插入图片描述

主要功能:
  • Authentication : 认证 , 验证用户是否合法 . 也就是”登陆” ,
  • Authorization : 授权 , 登陆成功之后 , 授予谁某些可以授予的权限
  • Session Management : 会话管理 , 用户登陆一次就是一次会话 . 用户登录后的用户信息通过会话管理进行管理
  • Cryptography : 加密 . 提供了常见的一些加密算法 . 在应用中更方便实现数据安全 , 使用便捷 .
支持的功能:
  • Web Support : Web应用程序支持 . shiro可以方便的集成到web应用中.
  • Caching : 缓存 , shiro提供了对缓存的支持 . shiro提供对缓存的支持 , 支持多种缓存架构 , 如Redis.
  • Concurrency : 多线程 . 支持多线程并发访问.
  • Testing : 测试
  • “Run As” : 权限传递 , 一个人以另外一个人登陆 . 一个张三 , 一个小张三 , 张三授予权限给小张三 , 小张三就可以登陆
  • Remember Me : 记住我 .

shiro架构

  • Subject : 实体(用户 , 第三方服务 , cron job计划任务 等), 传递数据到安全管理器 .
  • Security Manager : 安全管理器 . Shiro中最重要的核心 . 协调各个组件 , 如管理着认证管理器 , 授权管理器等的工作.
  • Authentication : 认证管理器 , 负责验证用户的身份
  • Authorization :授权管理器 . 负责为合法的用户指定其权限 . 控制用户可以访问那些资源 .
  • Realms : 域 , 和外界的数据库进行交互 . 用户通过Shiro来完成相关安全工作 , Shiro是不会去维护信息的 . 在Shiro中 , 数据的查询和获取工作是通过Realm 从不同的数据源来获取的 . Realm可以获取数据库信息和文本信息等 . Shiro中可以有一个或多个realm

Authentication 用户认证


1 . 认证逻辑

验证用户是否合法登陆
在这里插入图片描述

  • 用户(Subject )需要提交身份(Principals)和凭证(Credentials)给Security Manager : 安全管理器
    • 身份(Principals)能够唯一标识用户(Subject ) , 例如邮箱 , 身份证ID , 因为它独一无二
    • 凭证(Credentials)是只被用户(Subject )知道的一个值 , 例如密码 , 数字证书 . 它只被用户(Subject )拥有和知晓.
    • 而身份(Principals)和凭证(Credentials)最常见的组合就是: 用户名/密码 , Shiro中通常使用UsernamePasswordToken来制定身份和凭证信息

2 . 在shiro中用户的认证流程

在这里插入图片描述

UsernamePasswordToken ->安全管理器(Security Manager) ->认证管理器(Authentication) -> 根据所配置的realm调用策略 -> 根据策略调用realm

授权

  • 给身份认证通过的人 , 授予他可以访问某些资源的权限 权限的粒度 , 分为粗粒度和细粒度 .

    • 粗粒度 : 对user的增删查改,. 也就是说通常对表的操作 .
    • 细粒度 : 对记录的操作 . 如 : 只允许查询id为1的用户工资 .
  • shiro一般管理的是粗粒度的权限 . 比如:菜单 , 按钮 , url . 一般细粒度的权限是通过业务来控制的 .

  • 权限表示规则:

    • 通配符
      资源:操作:实例 ->
      user:add 表示用户有添加的权限
      user:* 表示user具有用户的所有操作权限
      user:delete:100 表示用户对user标识为100的记录有删除权限
    • 8421
      0001
      0010
      0100
      1000
      (增删查改)

代码实现


1 . 依赖(基于springboot2和shiro1.3.2)

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

2 . 配置类

package com.credi.config;

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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @Author: He Xingchi
 * Created by Administrator on 2019/10/7.
 * 对shiro的一些配置,相对于之前的xml配置。包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。
 */
@Configuration
public class ShiroConfig {
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
//        登录接口
        shiroFilterFactoryBean.setLoginUrl("/login");
//        登录失败跳转接口
        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/front/**", "anon");
        filterChainDefinitionMap.put("/api/**", "anon");

        filterChainDefinitionMap.put("/admin/**", "authc");
//        filterChainDefinitionMap.put("/user/**", "authc");
        filterChainDefinitionMap.put("/user", "authc");
        filterChainDefinitionMap.put("/test/**", "authc");
        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;

    }

    /*
    Security Manager : 安全管理器 .
    Shiro中最重要的核心 . 协调各个组件 , 如管理着认证管理器 , 授权管理器等的工作.
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customRealm());
        return defaultSecurityManager;
    }

    //不加这个注解不生效,具体不详
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }
    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


//    自己定义的授权域
    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }


}

3 . 自定义Realm,身份验证和权限验证策略

package com.credi.config;

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;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: He Xingchi
 * Created by Administrator on 2019/10/7.
 * 自定义的CustomRealm继承AuthorizingRealm。
 * 并且重写父类中的doGetAuthorizationInfo(权限相关)、doGetAuthenticationInfo(身份认证)这两个方法。
 */
public class CustomRealm extends AuthorizingRealm {
    /**
     * 身份认证
     * 这里可以注入userService,为了方便演示,我就写死了帐号了密码
     * private UserService userService;
     * <p>
     * 获取即将需要认证的信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("-------身份认证方法--------");
//        获取UsernamePasswordToken中用户输入的用户名
        String userName = (String) authenticationToken.getPrincipal();
        String userPwd = new String((char[]) authenticationToken.getCredentials());
        //根据用户名从数据库获取校验的用户信息
        String password = "123";
        if (userName == null) {
//            抛出对应的异常
            throw new AccountException("用户名不正确");
        } else if (!userPwd.equals(password )) {
            throw new AccountException("密码不正确");
        }
//  从数据库中查询的信息封装到 -> new SimpleAuthenticationInfo(userName, password,getName()) -> shiro框架进行信息的校验
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, password, getName());// getName() -> realm或自定义realm类名称
//            也可以吧查询到的用户信息存入 new SimpleAuthenticationInf(new User() , password , getName());
//        返回认证信息
        return simpleAuthenticationInfo;
    }

//    权限认证
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("-------权限认证方法--------");
        //身份认证时 , 存入的信息可以被获取
        //        //获取用户名
//        User user = (User)SecurityUtils.getSubject().getPrincipal();
//        String username = (String) SecurityUtils.getSubject().getPrincipal();
        String username = (String) principals.getPrimaryPrincipal();
        //根据身份信息从数据库获取权限数据  == > 模拟
        List<String> permissions = new ArrayList<String>();
        permissions.add("admin");
        permissions.add("user:save");
        permissions.add("user:delete");
        permissions.add("userInfo:view");

//      将权限信息保存到shiro中的AuthorizationInfo中
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        list添加到AuthorizationInfo
        for(String permission:permissions){
            System.out.println(permission);
            info.addStringPermission(permission);
        }
//       set添加到AuthorizationInfo
//        Set<String> stringSet = new HashSet<>();
//        stringSet.add("user:show");
//        stringSet.add("user:admin");
//        info.setStringPermissions(stringSet);
        return info;
    }


}

4 . 用户身份认证(登陆)测试类

package com.credi.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.*;

/**
 * @Author: He Xingchi
 * Created by Administrator on 2019/10/7.
 */
@RestController
@RequestMapping
public class IndexController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    @ResponseBody
    public String defaultLogin() {
        return "首页";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
        // 从SecurityUtils里边创建一个 subject
        Subject subject = SecurityUtils.getSubject();
        // 在认证提交前准备 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 执行认证登陆
        try {
            subject.login(token);
        } catch (UnknownAccountException uae) {
            return "未知账户";
        } catch (IncorrectCredentialsException ice) {
            return "密码不正确";
        } catch (LockedAccountException lae) {
            return "账户已锁定";
        } catch (ExcessiveAttemptsException eae) {
            return "用户名或密码错误次数过多";
        } catch (AuthenticationException ae) {
            return "用户名或密码不正确!";
        }
        if (subject.isAuthenticated()) {
            return "登录成功";
        } else {
            token.clear();
            return "登录失败";
        }
    }

}

5 . 用户权限验证测试类

package com.credi.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;

/**
 * @Author: He Xingchi
 * Created by Administrator on 2019/10/8.
 */
@RestController
@RequestMapping
public class UserController {

    @RequestMapping(value = "/user", method = RequestMethod.POST)
    @ResponseBody
    @RequiresPermissions("userInfo:view") // 测试权限
    public String user(@RequestParam("msg") String msg) {
       return msg;
    }
}

ps : 常用内置权限函数

// 用户认证状态
		boolean isAuthenticated = subject.isAuthenticated();
		System.out.println("用户认证状态:" + isAuthenticated);
		
		//判断拥有角色:role1
		Assert.assertTrue(subject.hasRole("role1"));
		//判断拥有角色:role1 and role2
		Assert.assertTrue(subject.hasAllRoles(Arrays.asList("role1", "role2")));
		//判断拥有角色:role1 and role2 and !role3
		boolean[] result = subject.hasRoles(Arrays.asList("role1", "role2", "role3"));
		Assert.assertEquals(true, result[0]);
		Assert.assertEquals(true, result[1]);
		Assert.assertEquals(false, result[2]);
		
		
		//判断拥有权限:user:create
		Assert.assertTrue(subject.isPermitted("user:create"));
		//判断拥有权限:user:update and user:delete
		Assert.assertTrue(subject.isPermittedAll("user:update", "user:delete"));
		//判断没有权限:user:view
		Assert.assertFalse(subject.isPermitted("user:view"));

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值