Java研学-Shiro安全框架(五)

七 SpringBoot集成Shiro鉴权

1 Shiro 鉴权三种方式

  编程式 通过写 if/else 授权代码块完成

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("hr")) {
	//有权限
} else {
	//无权限
}

  注解式 通过在controller的方法上放置相应的注解完成(shiro已经做好了,直接贴就行)

@RequiresRoles("hr") 
@RequiresPermissions("user:create") 
// 设置多个权限是否同时需要,或者只有其中一种即可
// @RequiresPermissions(value={"user:create","user:delete"},logical=logical.OR) 
public void addUser(User user) {
    //有权限
}

  JSP标签(shiro自带) 、Freemarker的标签(第三方) 、ThymeLeaf的标签(第三方)在页面通过相应的标签完成,ThymeLeaf标签文档,通过标签完成有权限显示,无权限不显示。

<a shiro:hasRole="administrator" href="admin.html">Administer the system</a>
<a shiro:hasPermission="user:create" href="createUser.html">Create a new User</a>

2 Shiro 拦截方法流程

在这里插入图片描述

3 支持注解鉴权配置

  需要ShiroConfig中配置Shiro注解通知器Bean(当看到注解后通知SecurityManager进行权限认证),与支持CGLIB的Bean

// 开启Shiro注解通知器
// Qualifier(起别名)此处 SecurityManager 可能报错,因为lang包是自动导入的
// 所以此处的 SecurityManager 是lang包下的,我们需要的是Shiro包下的 SecurityManager 可使用acap.xx.xx.xx.SecurityManager 这种方式,
// 或者使用之前定义的安全管理器类型 DefaultWebSecurityManager 防止自动导包
// public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
//        DefaultWebSecurityManager securityManager){}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
        @Qualifier("securityManager") SecurityManager securityManager)
{
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}
// 设置支持CGlib代理
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    advisorAutoProxyCreator.setProxyTargetClass(true);
    return advisorAutoProxyCreator;
}

4 完善Realm的授权方法

  在Apache Shiro中,SimpleAuthorizationInfo 并不直接与 Subject 关联。相反,SimpleAuthorizationInfo 是用于封装授权信息(如角色和权限)的,这些信息随后会被存储在 SecurityManager 的上下文中,以便在需要时与 Subject 进行关联和检查

//授权相关
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
	// 获取登录用户
    Employee employee = (Employee)principals.getPrimaryPrincipal();
    // 封装授权信息类
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //根据用户的id查询该用户拥有的角色编码
    List<Role> roles = roleService.queryByEmployeeId(employee.getId());
    for(Role role:roles){
        info.addRole(role.getSn());
    }
    //根据用户的id查询该用户拥有的权限表达式
    List<String> permissions = permissionService.queryByEmployeeId(employee.getId());
    info.addStringPermissions(permissions);
    return info;
}

5 完善自定义异常 – 无权限异常

@ControllerAdvice
public class ExceptionControllerAdvice {
    // 自定义异常(向让用户看到的)
    @ExceptionHandler(BusinessException.class)
    public String handlerException(BusinessException e, Model model, HandlerMethod method, HttpServletResponse response){
        // 异常也分为页面异常和 ajax 请求的异常。
        if(method.hasMethodAnnotation(ResponseBody.class)){
            // ajax 请求
            response.setContentType("application/json;charset=utf-8");
            try {
                response.getWriter().write(new ObjectMapper().writeValueAsString(new JsonResult(false,e.getMessage())));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return null;
        } else{
            // 页面请求
            model.addAttribute("errorMsg",e.getMessage());
            return "common/error";
        }
    }

    // 系统异常(不想让用户看到的)
    @ExceptionHandler(Exception.class)
    public String handlerException(Exception e, Model model, HandlerMethod method, HttpServletResponse response){
        // 异常也分为页面异常和 ajax 请求的异常。
        if(method.hasMethodAnnotation(ResponseBody.class)){
            // ajax 请求
            response.setContentType("application/json;charset=utf-8");
            try {
                response.getWriter().write(new ObjectMapper().writeValueAsString(new JsonResult(false,"系统繁忙,请联系管理员")));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return null;
        } else{
            // 页面请求
            model.addAttribute("errorMsg","系统繁忙,请联系管理员");
            return "common/error";
        }
    }

    // 没有权限异常
    @ExceptionHandler(UnauthorizedException.class)
    public String handlerException(UnauthorizedException e, Model model, HandlerMethod method, HttpServletResponse response){
        // 异常也分为页面异常和 ajax 请求的异常。
        if(method.hasMethodAnnotation(ResponseBody.class)){
            // ajax 请求
            response.setContentType("application/json;charset=utf-8");
            try {
                response.getWriter().write(new ObjectMapper().writeValueAsString(new JsonResult(false,"您还没有权限访问,请联系管理员")));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return null;
        } else{
            // 页面请求
            model.addAttribute("errorMsg","您还没有权限访问,请联系管理员");
            return "common/nopermission";
        }
    }
}

6 超级管理员权限

  超级管理员并没有配置任务的角色和权限会被拦截。所以需要在授权代码中做特殊的处理

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    Employee employee = (Employee)principals.getPrimaryPrincipal();
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    if(employee.isAdmin()){
        //如果是超级管理员,给管理员赋所有的权限
        List<Role> roles = roleService.listAll();
        for(Role role:roles){
            info.addRole(role.getSn());
        }
        //使用通配符表示拥有所有的权限
        info.addStringPermission("*:*");
    }else{
        //根据用户的id查询该用户拥有的角色编码
        List<Role> roles = roleService.queryByEmployeeId(employee.getId());
        for(Role role:roles){
            info.addRole(role.getSn());
        }
        //根据用户的id查询该用户拥有的权限表达式
        List<String> permissions = permissionService.queryByEmployeeId(employee.getId());
        info.addStringPermissions(permissions);
    }
    return info;
}

7 ShringBoot集成Shiro实现注解式鉴权功能步骤

  1. 在需要权限控制的控制器方法上面贴上注解。
    • @RequiresPermissions() ==> 需要权限控制
    • @RequiresRoles() ==> 需要角色控制
  2. 由于 Shiro 中使用的是 AOP 的方式对方法进行拦截控制,所以我们需要在配置文件中配置注解通知器,配置支持CGLIB。
  3. 完善 Realm 中授权逻辑
    • 根据用户名查询当前用户信息。
    • 根据用户信息查询用户的权限集合和角色集合。
  4. 对于超级管理员应该拥有一切的角色和权限。(角色必须通过遍历才能添加,权限可以使用通配符)

8 编程式鉴权

  这种方式允许开发者根据业务需求自定义鉴权逻辑,具有较高的灵活性和可定制性。(注解式必须抛异常,编程式可不抛)

@RequestMapping("/list")
public String list(Model model, QueryObject qo) {
    Subject subject = SecurityUtils.getSubject();
    PageInfo<Department> pageInfo = null;
    if(subject.isPermitted("department:list")){
        pageInfo = departmentService.query(qo);
    }else{
        pageInfo = new PageInfo<>(Collections.EMPTY_LIST);
    }
    model.addAttribute("pageInfo", pageInfo);
    return "department/list";
}

9 标签式鉴权

  ① 需要依赖(可以不是thymeleaf)

<!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency>
	<groupId>com.github.theborakompanioni</groupId>
	<artifactId>thymeleaf-extras-shiro</artifactId>
	<version>${thymeleaf.extras.shiro.version}</version>
</dependency>

  ② 配置Shiro集成ThymeLeaf标签支持(ShiroConfig中添加Bean)

// thymeleaf模板引擎和shiro框架的整合
@Bean
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}

  ③ 页面中添加约束头

<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

  ④ 使用标签

<a href="#" class="btn btn-success btn-input" style="margin: 10px" shiro:hasPermission="department:saveOrUpdate">
    <span class="glyphicon glyphicon-plus"></span> 添加
</a>
  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泰勒疯狂展开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值