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. dns配置文件的方式

    1./etc/resolve.conf /etc/resolv.conf它是DNS客户机配置文件,用于设置DNS服务器的IP地址及DNS域名,还包含了主机的域名搜索顺序.该文件是由域名解析 器(res ...

  2. SpringBoot整合Apache-CXF实践

    一.Apache CXF是什么? Apache CXF 是一个开源的 Services 框架,CXF 帮助您利用 Frontend 编程 API 来构建和开发 Services ,像 JAX-WS . ...

  3. 给大家分享一下java数据库操作步骤

    获取驱动程序Jar文件,并放置到项目的类路径中: 注册驱动器类: 获取数据库连接: 获取Statement对象来执行相关SQL操作: 关闭各种资源;

  4. Kubernetes Scheduler浅析

    概述 Kubernetes 调度器(Scheduler)是Kubernetes的核心组件:用户或者控制器创建Pod之后,调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被 ...

  5. RestfulApi 学习笔记——简单介绍(一)

    前言 什么是restapi? 直接看:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 阮一峰的blog,即可明白,下面是一些例子,增强理 ...

  6. MySQL全面瓦解5:数据操作-DML

    说明 DML(Data Manipulation Language)数据操作语言,是指对数据库进行增删改的操作指令,主要有INSERT.UPDATE.DELETE三种,代表插入.更新与删除,这是学习M ...

  7. NB-IoT的数据链路层和上行传输信道类型

    NB-IoT的数据链路层 NB-IoT在LTE系统的基础上对数据链路层进行了大量简化,但整体上还是保持了原有的框架.数据链路层是二层协议,包含了3个子层:MAC子层.RLC子层和PDCP子层. 以网络 ...

  8. Nginx 配置请求响应时间

    1.常见默认nginx.conf配置日志格式 log_format main '$remote_addr - $remote_user [$time_local] "$request&quo ...

  9. python获取汉字首字母

    获取汉字首字母 关注公众号"轻松学编程"了解更多. 应用场景之一:可用于获取名字首字母,在数据库中查询记录时,可以用它来排序输出. from pytz import unicode ...

  10. glog修改

    在写代码的过程中,打log肯定是少不了的,毕竟不能总靠调试来发现问题.log库的选用就很纠结了,成熟的log库非常多,log4cpp.log4cxx.poco.log.boost.log.glog等等 ...