SpringSecurity之授权
SpringSecurity之授权
[toc]
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 3.2入门(10)自定义权限控制认证及授权的过程
- oauth2授权,配置springSecurity web认证
- SpringSecurity(1)---认 2088 证+授权代码实现
- SpringSecurity用户认证和授权Demo
- SpringSecurity | 源码分析篇 (二) 授权过程
- SpringSecurity授权管理介绍
- 使用SpringSecurity搭建授权认证服务(1) -- 基本demo认证原理
- SpringBoot--- 使用SpringSecurity进行授权认证
- SpringSecurityOAuth2.0授权类型配置
- Asp.net中基于Forms验证的角色验证授权
- Acegi授权策略和保护web资源
- Java微信公众号--3--网页授权的一些问题
- HttpWebRequest访问时,错误:(401)未经授权。
- 基于Swift语言开发微信、QQ和微博的SSO授权登录代码分析
- mysql授权
- (转)远程桌面连接问题“由于授权协议中的一个错误,远程计算机中断了会话”
- mysql新建用户及授权
- shiro权限框架中的认证和授权过程
- MySql增加用户、授权、修改密码等语句