SpringSecurity之授权
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">-->
<!-- <!– 内容主体区域 –>-->
<!-- <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之授权的更多相关文章
- 使用SpringSecurity搭建授权认证服务(1) -- 基本demo认证原理
使用SpringSecurity搭建授权认证服务(1) -- 基本demo 登录认证是做后台开发的最基本的能力,初学就知道一个interceptor或者filter拦截所有请求,然后判断参数是否合理, ...
- SpringBoot--- 使用SpringSecurity进行授权认证
SpringBoot--- 使用SpringSecurity进行授权认证 前言 在未接触 SpringSecurity .Shiro 等安全认证框架之前,如果有页面权限需求需要满足,通常可以用拦截器, ...
- [原创]SpringSecurity控制授权(鉴权)功能介绍
1.spring security 过滤器链 spring security中的除了用户登录校验相关的过滤器,最后还包含了鉴权功能的过滤器,还有匿名资源访问的过滤器链,相关的图解如下: 2.控制授 ...
- springsecurity4+springboot 实现remember-me 发现springsecurity 的BUG
前言:现在开发中,记住我这个功能是普遍的,用户不可能每次登录都要输入用户名密码.昨天准备用spring security的记住我功能,各种坑啊,吐血 . 先看下具体实现吧. spring securi ...
- SpringBoot19 集成SpringSecurity01 -> 环境搭建、SpringSecurity验证
1 环境搭建 1.1 创建一个SpringBoot项目 项目脚手架 -> 点击前往 1.2 创建一个Restful接口 新建一个Controller类即可 package com.example ...
- spring-security实现的token授权
在我的用户密码授权文章里介绍了spring-security的工作过程,不了解的同学,可以先看看用户密码授权这篇文章,在 用户密码授权模式里,主要是通过一个登陆页进行授权,然后把授权对象写到sessi ...
- 基于SpringSecurity和JWT的用户访问认证和授权
发布时间:2018-12-03 技术:springsecurity+jwt+java+jpa+mysql+mysql workBench 概述 基于SpringSecurity和JWT的用户访 ...
- 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 环境搭建 创建一 ...
- SpringBoot+SpringSecurity之多模块用户认证授权同步
在之前的文章里介绍了SpringBoot和SpringSecurity如何继承.之后我们需要考虑另外一个问题:当前微服务化也已经是大型网站的趋势,当我们的项目采用微服务化架构时,往往会出现如下情况: ...
随机推荐
- Linux命令之命令别名
对于经常执行的较长的命令,可以将其定义成较短的别名,以方便执行 显示当前shell进程所有可用的命令别名 [04:33:43 root@C8[ ~]#alias alias cp='cp -i' al ...
- log4j日志级别怎么搞
日志的级别之间的大小关系如右所示:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF Log4j建 ...
- SpringBoot连接多数据源(HBASE,KUDU,ORACLE集成和开发库)
前提:1.连接hadoop需要本地安装 winutils.exe 并在程序指定,不然程序会报错 IOException: HADOOP_HOME or hadoop.home.dir are not ...
- vue自定义指令长按事件
Vue.directive('longpress', { bind: function (el, binding, vNode) { // Make sure expressi ...
- Java学习的第三十五天
1. 例1.1 例1.2 例1.3 例1.4 2.不知道为什么con的时候错误需要加入 3.明天继续写例题
- python造一个计算器
正则表达式之简易计算器 关注公众号"轻松学编程"了解更多. 需求:使用正则表达式完成一个简易计算器. 功能:能够计算简单的表达式. 如:12((1+2)/(2+3)+1)*5.1- ...
- Flask常用扩展(Extentions)
Flask常用扩展(Extentions) 官网;http://flask.pocoo.org/extensions/ 1.Flask-Script 说明: 一个flask终端运行的解析器 安装: ...
- 抓紧下载了!2020最新版《神经网络与深度学习》中文版,PDF免费开放下载
介绍<神经⽹络和深度学习>是⼀本免费的在线书,对读者数学知识需求适度,兼顾理论和动手实践.⽬前给出了在图像识别.语⾳识别和⾃然语⾔处理领域中很多问题的最好解决⽅案,教读者在神经⽹络和深度学 ...
- expect ':' at 0, actual = (JSON转化异常解决)
这个报错我的问题主要是前端得到的JSON格式不是标准的JSON串,所以会报这个错, 解决办法 需要使用JSON.toJSONString()转换为标准的字符串
- from `float` to `np.floating` is deprecated
运行tensorflow测试程序时,出现:FutureWarning: Conversion of the second argument of issubdtype from `float` to ...