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. Linux命令之命令别名

    对于经常执行的较长的命令,可以将其定义成较短的别名,以方便执行 显示当前shell进程所有可用的命令别名 [04:33:43 root@C8[ ~]#alias alias cp='cp -i' al ...

  2. log4j日志级别怎么搞

    日志的级别之间的大小关系如右所示:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF Log4j建 ...

  3. SpringBoot连接多数据源(HBASE,KUDU,ORACLE集成和开发库)

    前提:1.连接hadoop需要本地安装 winutils.exe 并在程序指定,不然程序会报错  IOException: HADOOP_HOME or hadoop.home.dir are not ...

  4. vue自定义指令长按事件

    Vue.directive('longpress', {     bind: function (el, binding, vNode) {         // Make sure expressi ...

  5. Java学习的第三十五天

    1. 例1.1 例1.2 例1.3 例1.4 2.不知道为什么con的时候错误需要加入 3.明天继续写例题

  6. python造一个计算器

    正则表达式之简易计算器 关注公众号"轻松学编程"了解更多. 需求:使用正则表达式完成一个简易计算器. 功能:能够计算简单的表达式. 如:12((1+2)/(2+3)+1)*5.1- ...

  7. Flask常用扩展(Extentions)

    Flask常用扩展(Extentions) 官网;http://flask.pocoo.org/extensions/ 1.Flask-Script ​ 说明: 一个flask终端运行的解析器 安装: ...

  8. 抓紧下载了!2020最新版《神经网络与深度学习》中文版,PDF免费开放下载

    介绍<神经⽹络和深度学习>是⼀本免费的在线书,对读者数学知识需求适度,兼顾理论和动手实践.⽬前给出了在图像识别.语⾳识别和⾃然语⾔处理领域中很多问题的最好解决⽅案,教读者在神经⽹络和深度学 ...

  9. expect ':' at 0, actual = (JSON转化异常解决)

    这个报错我的问题主要是前端得到的JSON格式不是标准的JSON串,所以会报这个错, 解决办法 需要使用JSON.toJSONString()转换为标准的字符串

  10. from `float` to `np.floating` is deprecated

    运行tensorflow测试程序时,出现:FutureWarning: Conversion of the second argument of issubdtype from `float` to ...