SpringSecurity之授权

1. 写在前面的话

此文并非教程, 而是个人学习SpringSecurity时的记录以及一些踩坑解决

如果能帮到大家, 那就让人非常开心了

另外, SpringSecurity的授权分为web授权和方法授权, 本文只说明了web授权, 方法授权实际上就是SpringSecurity控制对方法和接口的访问, 需要学习的小伙伴可以自行学习, 笔者在今后的工作中如果用到的话也会回来添加相关内容

好了, 让我们看看笔者的一些学习心得吧

2. web授权

个人认为授权较之认证简单了许多, 大概是本人在认证把该踩的坑都踩了一遍吧...

首先, 我们需要建立数据库

1. 建库

我们需要以下的几张表

  • 角色表

  • 权限表

  • 用户表(这个在认证中已经建立了)

  • 角色权限关联表 (此处为管理员有1和2两个权限)

  • 用户角色关联表

    • 关联表是为了方便拓展, 正常的业务都是这样的, 存在一对多和多对多的关系

2. 添加查询权限的接口

权限的信息也是存放在UserDetails中的, 因此我们也要实现通过用户id查询用户对应权限的功能

  • 建立对应的实体类(此处省略)

  • 添加接口以及其实现类

    • dao

      //根据用户id查询用户权限
      List<PermissionDTO> getPermissionByUserId(String userId);
    • service接口

      List<PermissionDTO> getPermissionByUserId(String userId);
    • 接口实现类

      @Override
      public List<PermissionDTO> getPermissionByUserId(String userId) {
      return userMapper.getPermissionByUserId(userId);
      }
  • 在xml中添加查询的sql

    <!--根据用户id查询用户权限-->
    <select id="getPermissionByUserId" parameterType="string" resultType="permissionDTO">
    select *
    from t_permission
    where `id` in (
    select permission_id from t_role_permission
    where role_id = (
    select role_id from t_user_role
    where user_id = #{userId}
    )
    )
    </select>
    • 注意, 这里我们使用了子查询, 同时, 由于会查出多条结果, 我们要酌情考虑是否要使用 in 语句, 将多个结果放在select语句的条件中

3. 前端页面的编写

我们使用的是 layui的默认后端模板

  • 模板的定义

    先编写一个后台模板文件

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>layout 后台大布局 - Layui</title>
    <link rel="stylesheet" th:href="@{css/layui.css}">
    </head>
    <body class="layui-layout-body">
    <div class="layui-layout layui-layout-admin">
    <div class="layui-header">
    <div class="layui-logo">layui 后台布局</div>
    <!-- 头部区域(可配合layui已有的水平导航) -->
    <ul class="layui-nav layui-layout-left">
    <li class="layui-nav-item"><a href="">控制台</a></li>
    <li class="layui-nav-item"><a href="">商品管理</a></li>
    <li class="layui-nav-item"><a href="">用户</a></li>
    <li class="layui-nav-item">
    <a href="javascript:;">其它系统</a>
    <dl class="layui-nav-child">
    <dd><a href="">邮件管理</a></dd>
    <dd><a href="">消息管理</a></dd>
    <dd><a href="">授权管理</a></dd>
    </dl>
    </li>
    </ul>
    <ul class="layui-nav layui-layout-right">
    <li class="layui-nav-item">
    <a href="javascript:;">
    <img src="http://t.cn/RCzsdCq" class="layui-nav-img">
    <span sec:authentication="principal.username"></span>
    </a>
    <dl class="layui-nav-child">
    <dd><a href="">基本资料</a></dd>
    <dd><a href="">安全设置</a></dd>
    </dl>
    </li>
    <li class="layui-nav-item"><a id="logout" href="javascript:void(0);" onclick="logout()">退了</a></li>
    </ul>
    </div> <div class="layui-side layui-bg-black">
    <div class="layui-side-scroll">
    <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
    <ul class="layui-nav layui-nav-tree" lay-filter="test">
    <li class="layui-nav-item layui-nav-itemed">
    <a class="" href="javascript:;">所有商品</a>
    <dl class="layui-nav-child">
    <dd><a href="javascript:;">列表一</a></dd>
    <dd><a href="javascript:;">列表二</a></dd>
    <dd><a href="javascript:;">列表三</a></dd>
    <dd><a href="">超链接</a></dd>
    </dl>
    </li>
    <li class="layui-nav-item">
    <a href="javascript:;">解决方案</a>
    <dl class="layui-nav-child">
    <dd><a href="javascript:;">列表一</a></dd>
    <dd><a href="javascript:;">列表二</a></dd>
    <dd><a href="">超链接</a></dd>
    </dl>
    </li>
    <li class="layui-nav-item"><a href="">云市场</a></li>
    <li class="layui-nav-item"><a href="">发布商品</a></li>
    </ul>
    </div>
    </div> <!-- <div class="layui-body">-->
    <!-- &lt;!&ndash; 内容主体区域 &ndash;&gt;-->
    <!-- <div style="padding: 15px;">内容主体区域</div>-->
    <!-- </div>--> <div class="layui-footer">
    <!-- 底部固定区域 -->
    layui.com - 底部固定区域
    </div>
    </div>
    <script type="text/javascript" th:src="@{js/jquery.min.js}"></script>
    <script type="text/javascript" th:src="@{js/jquery-ui.min.js}"></script>
    <script type="text/javascript" th:src="@{js/jquery.mockjax.js}"></script>
    <script th:src="@{layui.js}"></script>
    <script>
    //JavaScript代码区域
    layui.use('element', function () {
    var element = layui.element; }); function logout() {
    layui.use('layer', function () {
    //退出登录
    layer.confirm('确定要退出么?', {icon: 3, title: '提示'}, function (index) {
    //do something
    let url = '/logout';
    $.ajax({
    url: url,
    type: "post",
    dataType: "json",
    contentType: "application/json;charset=utf-8",
    success: function (data) {
    alert("进入success---");
    let code = data.code;
    let url = data.url;
    let msg = data.msg;
    if (code == 203) {
    alert(msg);
    window.location.href = url;
    } else {
    alert("未知错误!");
    }
    },
    error: function (xhr, textStatus, errorThrown) {
    alert("进入error---");
    alert("状态码:" + xhr.status);
    alert("状态:" + xhr.readyState); //当前状态,0-未初始化,1-正在载入,2-已经载入,3-数据进行交互,4-完成。
    alert("错误信息:" + xhr.statusText);
    alert("返回响应信息:" + xhr.responseText);//这里是详细的信息
    alert("请求状态:" + textStatus);
    alert(errorThrown);
    alert("请求失败");
    }
    });
    layer.close(index);
    });
    });
    }
    </script>
    </body>
    </html>
    • 注意

      • 我们为了获得SpringSecurity中的用户名, 使用了SpringSecurity的thymeleaf方言, 可以实现细粒度的控制(依据权限是否显示某些标签)
      • 引入命名空间, 这样就有代码提示了(不引入其实也无所谓~) xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
  • 测试用页面

    为了测试权限, 我们定义了三个按钮, 分别对应三个权限(我们在下一节的SpringSecurity配置类中可以看到)

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>页面</title>
    </head>
    <body class="layui-layout-body"> <div th:include="content/layout"></div> <div class="layui-layout layui-layout-admin">
    <div class="layui-body">
    <!-- 内容主体区域 -->
    <div style="padding: 15px;">成功应用模板!</div>
    <div class="layui-btn-container">
    <button type="button" class="layui-btn" onclick="btnClick1()">按钮一</button>
    <button type="button" class="layui-btn" onclick="btnClick2()">按钮二</button>
    <button type="button" class="layui-btn" onclick="btnClick3()">按钮三</button>
    </div>
    </div>
    </div>
    <script>
    $.ajaxSetup({
    type: "post",
    dataType: "json",
    contentType: "application/json;charset=utf-8",
    success: function (data) {
    let code = data.code;
    let url = data.url;
    let msg = data.msg;
    if (code == 204) {
    alert(msg);
    window.location.href = url;
    } else {
    alert("未知错误!");
    }
    },
    error: function (xhr, textStatus, errorThrown) {
    alert("进入error---");
    alert("状态码:" + xhr.status);
    alert("状态:" + xhr.readyState); //当前状态,0-未初始化,1-正在载入,2-已经载入,3-数据进行交互,4-完成。
    alert("错误信息:" + xhr.statusText);
    alert("返回响应信息:" + xhr.responseText);//这里是详细的信息
    alert("请求状态:" + textStatus);
    alert(errorThrown);
    alert("请求失败");
    }
    }); function btnClick1() {
    let url = "/toR1";
    $.ajax({
    url: url,
    type: "post",
    dataType: "json",
    contentType: "application/json;charset=utf-8", });
    } function btnClick2() {
    let url = "/toR2";
    $.ajax({
    url: url,
    type: "post",
    dataType: "json",
    contentType: "application/json;charset=utf-8", });
    } function btnClick3() {
    let url = "/toR3";
    $.ajax({
    url: url,
    type: "post",
    dataType: "json",
    contentType: "application/json;charset=utf-8", });
    }
    </script>
    </body>
    </html>

4. SpringSecurity配置

//授权
http
.authorizeRequests()
.antMatchers("/r/r1").hasAnyAuthority("p1")
.antMatchers("/r/r2").hasAnyAuthority("p2")
.antMatchers("/r/r3").access("hasAuthority('p1') and hasAuthority('p2')")
.antMatchers("/r/**").authenticated().anyRequest().permitAll();

在配置类中配置授权的规则

注意

  • Authority和Role都是角色管理, 区别是Role会加一个 ROLE_ 前缀, 具体的区别可以在网上自行查看, 一般来说, 我们使用Authority就行了

5. Controller配置

  • RestController处理AJAX
package com.wang.spring_security_framework.controller;

import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import java.util.HashMap;
import java.util.Map; @RestController
public class RestfulJumpController {
@RequestMapping("/toR1")
public String toR1Page() {
String url = "/r/r1";
String code = "204";
String msg = "即将跳转!";
Map<String, String> resultMap = new HashMap<>();
resultMap.put("url", url);
resultMap.put("code", code);
resultMap.put("msg", msg);
return JSON.toJSONString(resultMap);
} @RequestMapping("/toR2")
public String toR2Page() {
String url = "/r/r2";
String code = "204";
String msg = "即将跳转!";
Map<String, String> resultMap = new HashMap<>();
resultMap.put("url", url);
resultMap.put("code", code);
resultMap.put("msg", msg);
return JSON.toJSONString(resultMap);
} @RequestMapping("/toR3")
public String toR3Page() {
String url = "/r/r3";
String code = "204";
String msg = "即将跳转!";
Map<String, String> resultMap = new HashMap<>();
resultMap.put("url", url);
resultMap.put("code", code);
resultMap.put("msg", msg);
return JSON.toJSONString(resultMap);
} }

我们用RestController处理了Ajax的跳转请求, 并返回了JSON信息, 让前端进行url跳转

  • 测试的授权Controller

这里的Controller用于显示对应的资源目录下的一些内容, 其中我们从会话中获取了当前登录的用户名

package com.wang.spring_security_framework.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping("/r")
public class AuthorizeTestController { //从会话中获取当前登录用户名
private String getUserName(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//未登录, 返回null
if(!authentication.isAuthenticated()) {
return null;
}
Object principal = authentication.getPrincipal();
String username;
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else {
username = principal.toString();
}
return username;
} @RequestMapping("/r1")
public String R1() {
return getUserName() + "访问资源1";
} @RequestMapping("/r2")
public String R2() {
return getUserName() + "访问资源2";
} @RequestMapping("/r3")
public String R3() {
return getUserName() + "访问资源3";
}
}

3. 结语

本篇看起来很简单, 事实上也确实很简单......

主要需要理解的是数据库的建立以及关联表的处理, 其中的SQL语句才是最难搞的

访问没有授权的页面, 会爆出 405 错误, 我们可以自定义错误页面来处理这种错误(此处就不叙述了, 属于SpringBoot的内容~)

欢迎小伙伴批评与交流~

SpringSecurity之授权的更多相关文章

  1. 使用SpringSecurity搭建授权认证服务(1) -- 基本demo认证原理

    使用SpringSecurity搭建授权认证服务(1) -- 基本demo 登录认证是做后台开发的最基本的能力,初学就知道一个interceptor或者filter拦截所有请求,然后判断参数是否合理, ...

  2. SpringBoot--- 使用SpringSecurity进行授权认证

    SpringBoot--- 使用SpringSecurity进行授权认证 前言 在未接触 SpringSecurity .Shiro 等安全认证框架之前,如果有页面权限需求需要满足,通常可以用拦截器, ...

  3. [原创]SpringSecurity控制授权(鉴权)功能介绍

    1.spring security 过滤器链 ​ spring security中的除了用户登录校验相关的过滤器,最后还包含了鉴权功能的过滤器,还有匿名资源访问的过滤器链,相关的图解如下: 2.控制授 ...

  4. springsecurity4+springboot 实现remember-me 发现springsecurity 的BUG

    前言:现在开发中,记住我这个功能是普遍的,用户不可能每次登录都要输入用户名密码.昨天准备用spring security的记住我功能,各种坑啊,吐血 . 先看下具体实现吧. spring securi ...

  5. SpringBoot19 集成SpringSecurity01 -> 环境搭建、SpringSecurity验证

    1 环境搭建 1.1 创建一个SpringBoot项目 项目脚手架 -> 点击前往 1.2 创建一个Restful接口 新建一个Controller类即可 package com.example ...

  6. spring-security实现的token授权

    在我的用户密码授权文章里介绍了spring-security的工作过程,不了解的同学,可以先看看用户密码授权这篇文章,在 用户密码授权模式里,主要是通过一个登陆页进行授权,然后把授权对象写到sessi ...

  7. 基于SpringSecurity和JWT的用户访问认证和授权

    发布时间:2018-12-03   技术:springsecurity+jwt+java+jpa+mysql+mysql workBench   概述 基于SpringSecurity和JWT的用户访 ...

  8. SpringSecurity04 利用JPA和SpringSecurity实现前后端分离的认证和授权

    1 环境搭建 1.1 环境说明 JDK:1.8 MAVEN:3.5 SpringBoot:2.0.4 SpringSecurity:5.0.7 IDEA:2017.02旗舰版 1.2 环境搭建 创建一 ...

  9. SpringBoot+SpringSecurity之多模块用户认证授权同步

    在之前的文章里介绍了SpringBoot和SpringSecurity如何继承.之后我们需要考虑另外一个问题:当前微服务化也已经是大型网站的趋势,当我们的项目采用微服务化架构时,往往会出现如下情况: ...

随机推荐

  1. Curl可以模拟浏览器

    curl直接访问被拒绝 [22:10:00 root@C7 ~]#curl -I www.163.com HTTP/1.1 403 Forbidden Date: Wed, 24 Jun 2020 0 ...

  2. Vue基础(1)

    Vue简介 1.JavaScript框架 2.简化Dom操作 3.响应式数据驱动 Vue基础 通过下面代码引用vue: <script src="https://cdn.jsdeliv ...

  3. Centos 7 firewall 命令

    Centos 7 firewall 命令: 查看已经开放的端口: firewall-cmd --list-ports 开启端口 firewall-cmd --zone=public --add-por ...

  4. SpringBoot+JPA+SpringSeurity+JWT

    目的:使用这个框架主要就是为了解决高并发环境下登陆操作对数据库及服务器的压力,同时能保证安全性: 加载时,SpringSecurity定义拦截器和添加两个Fitler: 登陆时,登陆成功,通过传入的信 ...

  5. 设计模式:策略模式(根据参数,调用不同的service接口)

    1.定义类容器providers,当spring加载时@PostConstruct(类似构造方法),会先执行init() : 在init()中,反射"cn.jdk18"的所有带自定 ...

  6. SAM学习笔记&AC自动机复习

    形势所迫,一个对字符串深恶痛绝的鸽子又来更新了. SAM 后缀自动机就是一个对于字符串所有后缀所建立起的自动机.一些优良的性质可以使其完成很多字符串的问题. 其核心主要在于每个节点的状态和$endpo ...

  7. vim编辑器使用简介

    使用格式 vim [option] /path/to/somefile ... option: -o水平分割 -O垂直分割 +打开后在最后一行 +Num打开后在地Num行,加号与Num之间不能有空格 ...

  8. java实现KFC点餐系统

    这篇文章主要为大家详细介绍了java实现KFC点餐系统,模拟肯德基快餐店的收银系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 同学们应该都去麦当劳或肯德基吃过快餐吧?请同学们参考肯德基官网的信 ...

  9. (数据科学学习手札98)纯Python绘制满满艺术感的山脊地图

    本文示例代码及附件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 下面的这幅图可能很多读者朋友们都看到过,这 ...

  10. sklearn.neighbors.NNeighborsClassifier 详细说明

    平时会用到sklearn.neighbors.NNeighborsClassifier函数来构建K最邻近分类器,所以这里对NNeighborsClassifier中的参数进行说明,文中参考的是scik ...