spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)
一,用数据库实现权限管理要注意哪些环节?
1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息,
避免页面上显示用户昵称时需要查数据库
2,如果需要在页面上显示用户的登录信息,
需要自定义一个interceptor,
把用户的昵称等信息添加到 modelandview
3,普通用户的角色,即默认的权限,因为每个用户都具有,
就不要写入到数据表中,
避免数据量大时查询缓慢
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,项目地址:
https://github.com/liuhongdi/securitylogin
2,项目功能说明
演示了使用数据库实现的用户RBAC权限管理
三种页面:
无权限限制页面:任何人都可访问
需登录页面:修改密码等:登录才可以访问
有权限限制页面:必须授予相应的角色后才能访问
3,项目结构:如图:



三,配置文件说明
1,pom.xml
<!--security begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--validation begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--mysql mybatis begin-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JSON解析fastjson begin-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
2,application.properties
#thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html #mysql
spring.datasource.url=jdbc:mysql://localhost:3306/security?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=lhddemo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #mybatis
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.mapper #error
server.error.include-stacktrace=always
#log
logging.level.org.springframework.web=trace
3,数据库:

表结构:
CREATE TABLE `sys_user` (
`userId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`userName` varchar(100) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
`nickName` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '昵称',
PRIMARY KEY (`userId`),
UNIQUE KEY `userName` (`userName`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'
添加数据 :
INSERT INTO `sys_user` (`userId`, `userName`, `password`, `nickName`) VALUES
(1, 'lhd', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '老刘'),
(2, 'admin', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '管理员'),
(3, 'merchant', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '商户老张');
说明:3个密码都是111111,仅供演示使用,大家在生产环境中一定不要这样设置
CREATE TABLE `sys_user_role` (
`urId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`userId` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`roleName` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '角色id',
PRIMARY KEY (`urId`),
UNIQUE KEY `userId` (`userId`,`roleName`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户角色关联表'
插入数据:
INSERT INTO `sys_user_role` (`urId`, `userId`, `roleName`) VALUES
(1, 2, 'ADMIN'),
(2, 3, 'MERCHANT');
四,java代码说明
1,WebSecurityConfig.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final static BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder();
@Resource
private UserLoginFailureHandler userLoginFailureHandler;//登录失败的处理类
@Resource
private UserLoginSuccessHandler userLoginSuccessHandler;//登录成功的处理类
@Resource
private UserLogoutSuccessHandler userLogoutSuccessHandler;//退出成功的处理类
@Resource
private UserAccessDeniedHandler userAccessDeniedHandler;//无权访问的处理类
@Resource
private SecUserDetailService secUserDetailService; //用户信息类,用来得到UserDetails //指定加密的方式,避免出现:There is no PasswordEncoder mapped for the id "null"
@Bean
public PasswordEncoder passwordEncoder(){//密码加密类
return new BCryptPasswordEncoder();
} @Override
protected void configure(HttpSecurity http) throws Exception {
//static
http.authorizeRequests()
.antMatchers("/css/**","/js/**","/img/**")//静态资源等不需要验证
.permitAll();
//permitall
http.authorizeRequests()
.antMatchers("/home/**")//permitall
.permitAll();
//login
http.formLogin()
.loginPage("/login/login")
.loginProcessingUrl("/login/logined")//发送Ajax请求的路径
.usernameParameter("username")//请求验证参数
.passwordParameter("password")//请求验证参数
.failureHandler(userLoginFailureHandler)//验证失败处理
.successHandler(userLoginSuccessHandler)//验证成功处理
.permitAll(); //登录页面用户任意访问
//logout
http.logout()
.logoutUrl("/login/logout")
.logoutSuccessUrl("/login/logout")
.logoutSuccessHandler(userLogoutSuccessHandler)//登出处理
.deleteCookies("JSESSIONID")
.clearAuthentication(true)
.invalidateHttpSession(true)
.permitAll();
//有角色的用户才能访问
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/merchant/**").hasAnyRole("MERCHANT","ADMIN"); //其他任何请求,登录后可以访问
http.authorizeRequests().anyRequest().authenticated();
//accessdenied
http.exceptionHandling().accessDeniedHandler(userAccessDeniedHandler);//无权限时的处理
//user detail
http.userDetailsService(secUserDetailService);
//rememberme
//图形验证码
//http.csrf().disable();
} @Resource
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(secUserDetailService).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return ENCODER.encode(charSequence);
}
//密码匹配,看输入的密码经过加密与数据库中存放的是否一样
@Override
public boolean matches(CharSequence charSequence, String s) {
return ENCODER.matches(charSequence,s);
}
});
}
}
2,SecUser.java
public class SecUser extends User {
//用户id
private int userid;
//用户昵称
private String nickname;
public SecUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public SecUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
}
spring security中User类的子类,增加了用户id和昵称,
需要保存到session中的信息,在这里扩展
目的是避免在每个页面上显示用户信息需要查数据库
3,SecUserDetailService.java
/**
* Created by liuhongdi on 2020/07/09.
*/
@Component("SecUserDetailService")
public class SecUserDetailService implements UserDetailsService{
@Resource
private SysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//查库
SysUser oneUser = sysUserService.getOneUserByUsername(s);//数据库查询 看用户是否存在
String encodedPassword = oneUser.getPassword();
Collection<GrantedAuthority> collection = new ArrayList<>();//权限集合
//用户权限:需要加 ROLE_
List<String> roles = oneUser.getRoles();
//System.out.println(roles);
for (String roleone : roles) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+roleone);
collection.add(grantedAuthority);
}
//增加用户的userid,nickname
SecUser user = new SecUser(s,encodedPassword,collection);
user.setUserid(oneUser.getUserId());
user.setNickname(oneUser.getNickName());
return user;
}
}
4,UserAccessDeniedHandler.java
@Component("UserAccessDeniedHandler")
public class UserAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AccessDeniedException e) throws IOException, ServletException {
boolean isAjax = ServletUtil.isAjax();
//System.out.println("isajax:"+isAjax);
if (isAjax == true) {
ServletUtil.printRestResult(RestResult.error(ResponseCode.ACCESS_DENIED));
} else {
ServletUtil.printString(ResponseCode.ACCESS_DENIED.getMsg());
}
}
}
5,UserLoginFailureHandler.java
@Component("UserLoginFailureHandler")
public class UserLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
//System.out.println("UserLoginFailureHandler");
ServletUtil.printRestResult(RestResult.error(ResponseCode.LOGIN_FAIL));
}
}
6,UserLoginSuccessHandler.java
@Component("UserLoginSuccessHandler")
public class UserLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//System.out.println("UserLoginSuccessHandler");
ServletUtil.printRestResult(RestResult.success(0,"登录成功"));
}
}
7,UserLogoutSuccessHandler.java
@Component("UserLogoutSuccessHandler")
public class UserLogoutSuccessHandler implements LogoutSuccessHandler{
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletRequest.getSession().invalidate();
ServletUtil.printRestResult(RestResult.success(0,"退出成功"));
}
}
8,WebInterceptor.java
@Component
public class WebInterceptor extends HandlerInterceptorAdapter {
//如果view不为空,把登录信息传递给模板
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
if (modelAndView != null) {
ModelMap modelMap = modelAndView.getModelMap();
SecUser currentUser = SessionUtil.getCurrentUser();
if (currentUser != null) {
modelMap.addAttribute("is_login","1");
modelMap.addAttribute("login_username",currentUser.getNickname());
} else {
modelMap.addAttribute("is_login","0");
modelMap.addAttribute("login_username","");
}
}
}
}
负责把传递页面公共部分显示的数据到模板
9,login.html
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录页面</title>
<script type="text/javascript" language="JavaScript" src="/js/jquery-1.6.2.min.js"></script>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
</style>
<!-- CSRF -->
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="/home/home"> 首页 </a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div class="starter-template">
<h2>使用账号密码登录</h2>
<div class="form-group">
<label for="username">账号</label>
<input type="text" class="form-control" id="username" name="username" value="" placeholder="账号" />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="password" placeholder="密码" />
</div>
<button name="formsubmit" value="登录" onclick="go_login()" >登录</button>
</div>
</div>
<script>
function go_login(){
if ($("#username").val() == "") {
alert('用户名不可为空');
$("#username").focus();
return false;
}
if ($("#password").val() == "") {
alert('密码不可为空');
$("#password").focus();
return false;
}
var postdata = {
username:$("#username").val(),
password:$("#password").val(),
}
var csrfToken = $("meta[name='_csrf']").attr("content");
var csrfHeader = $("meta[name='_csrf_header']").attr("content");
$.ajax({
type:"POST",
//type:"GET",
url:"/login/logined",
data:postdata,
//返回数据的格式
datatype: "json",//"xml", "html", "script", "json", "jsonp", "text".
beforeSend: function(request) {
request.setRequestHeader(csrfHeader, csrfToken); // 添加 CSRF Token
},
success:function(data){
if (data.code == 0) {
alert('login success:'+data.msg);
window.location.href="/home/home";
} else {
alert("failed:"+data.msg);
}
},
//调用执行后调用的函数
complete: function(XMLHttpRequest, textStatus){
},
//调用出错执行的函数
error: function(){
//请求出错处理
alert('error');
}
});
}
</script>
</body>
</html>
10,页面上用到的其他代码,可以移步github.com上查看
五,测试效果
1,访问首页:
http://127.0.0.1:8080/home/home
未登录时:

2,以普通用户lhd登录:

访问:管理员首页/商户首页,都会得到提示
无权访问
访问修改密码 页面,可以访问
3,以merchant用户登录:
role是MERCHANT

访问:管理员首页,提示:
无权访问
访问商户首页:可以访问
访问修改密码 页面,可以访问
4,以admin用户登录:
role是ADMIN

访问管理员首页:可以访问
访问商户首页:可以访问
访问修改密码 页面,可以访问
六,查看spring boot版本:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.1.RELEASE)
spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)的更多相关文章
- MySQL数据库用户和权限管理
一.视图 视图:VIEW,虚表,保存有实表的查询结果,在视图插入的内容都会存入表中.创建方法: CREATE VIEW view_name [(column_list)] AS select_st ...
- mysql数据库用户和权限管理记录
一.MySQL用户的基本说明: 1.1 用户的基本结构MySQL的用户:用户名@主机 ■用户名:16个字符以内■主机:可以是主机名.IP地址.网络地址等主机名:www.111cn.net,localh ...
- 第八章| 2. MySQL数据库|数据操作| 权限管理
1.数据操作 SQL(结构化查询语言),可以操作关系型数据库 通过sql可以创建.修改账号并控制账号权限: 通过sql可以创建.修改数据库.表: 通过sql可以增删改查数据: 可以通过SQL语句中 ...
- Spring Security实现RBAC权限管理
Spring Security实现RBAC权限管理 一.简介 在企业应用中,认证和授权是非常重要的一部分内容,业界最出名的两个框架就是大名鼎鼎的 Shiro和Spring Security.由于Spr ...
- 10款最好用的MySQL数据库客户端图形界面管理工具
MySQL Workbench 该工具由MySQL开发,是一个跨平台的可视化数据库设计工具.它是DBDesigner4项目备受期待的替代者,它是一个本地图形化工具,支持的操作系统包括Windows.L ...
- MySQL数据库以及表的管理
MySQL数据库以及表的管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 今天我们探讨的话题就是如何使用MySQL做开发,我们运维的主要工作不是去开发SQL的,但尽管如此,我们有 ...
- MySQL高级学习笔记(二):mysql配置文件、mysql的用户与权限管理、mysql的一些杂项配置
文章目录 mysql配置文件 二进制日志log-bin 错误日志log-error 数据文件 两系统 Myisam存放方式 innodb存放方式 如何配置 mysql的用户与权限管理 MySQL的用户 ...
- ylbtech-权限管理-数据库设计-功能权限管理技术
ylbtech-DatabaseDesgin:ylbtech-权限管理-数据库设计-功能权限管理技术 DatabaseName:ylb_permission(权限管理-功能权限管理技术)实现 Type ...
- spring boot:spring security用mysql实现动态权限管理(spring boot 2.3.3)
一,动态权限管理的优点和缺点 1,优点: 因为控制权限的数据保存在了mysql或其他存储系统中, 可以动态修改权限控制,无需改动代码和重启应用, 权限变更时灵活方便 2,缺点: 权限的设置需要保存在 ...
随机推荐
- 初识ABP vNext(9):ABP模块化开发-文件管理
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 创建模块 模块开发 应用服务 运行模块 单元测试 模块使用 最后 前言 在之前的章节中介绍过ABP扩展实体,当时在用户 ...
- DoS拒绝服务-工具使用hping3、nping等(四)
Hping3几乎可以定制发送任何tcp/ip数据包,用于测试fw,端口扫描,性能测试 Syn Flood – hping3 -c 1000 -d 120 -S -w 64 -p 80 --flood ...
- 吴恩达《深度学习》-第三门课 结构化机器学习项目(Structuring Machine Learning Projects)-第一周 机器学习(ML)策略(1)(ML strategy(1))-课程笔记
第一周 机器学习(ML)策略(1)(ML strategy(1)) 1.1 为什么是 ML 策略?(Why ML Strategy?) 希望在这门课程中,可以教给一些策略,一些分析机器学习问题的方法, ...
- 吴恩达《深度学习》-第一门课 (Neural Networks and Deep Learning)-第三周:浅层神经网络(Shallow neural networks) -课程笔记
第三周:浅层神经网络(Shallow neural networks) 3.1 神经网络概述(Neural Network Overview) 使用符号$ ^{[
- [算法]体积不小于V的情况下的最小价值(0-1背包)
题目 0-1背包问题,问要求体积不小于V的情况下的最小价值是多少. 相关 转移方程很容易想,初始化的处理还不够熟练,可能还可以更简明. 使用一维dp数组. 代码 import java.util.Sc ...
- web网站——nginx,LNMP部署03
nginx功能: (1)web服务器: 默认网页目录为:/usr/share/nginx/html (2)反向代理服务器: nginx代替客户端访问后端服务器,后端服务器只知道是nginx的请求,并将 ...
- android.support.v7.app.AppCompatActivity不能使用的解决办法
最近Android Studio 更新到4.0版本后,在构建项目时使用 android.support.v7.XX android.support.v4.XX 发现在xml文件中,原先我最常使用的Dr ...
- 刷题[GXYCTF2019]BabySQli
解题思路 只有一个登陆框,查看源码,链接到search.php后发现注释中有这样一段内容 MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSC ...
- 精心总结ansible-playbook剧本的这6种变量
#变量作用 #根据需求灵活修改,如:需要安装不同版本号的服务,或进行版本升级回退等 1.通过vars定义变量 #1.1.定义一个变量 version: 1.1.2 #定义多个变量 vars: - v1 ...
- Processing 网格(棋盘格)无限偏移纹理动画
过火 再度出击!这次我们要玩得更火一点---把静帧变动画.没错,将棋盘格动起来!看一下效果: 这是一个经典的无限偏移动画,在很多2d横版射击游戏中都会采用的技术.如何在Processing中实现,有两 ...