一,用数据库实现权限管理要注意哪些环节?

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)的更多相关文章

  1. MySQL数据库用户和权限管理

    一.视图 视图:VIEW,虚表,保存有实表的查询结果,在视图插入的内容都会存入表中.创建方法: CREATE VIEW view_name [(column_list)] AS select_st ...

  2. mysql数据库用户和权限管理记录

    一.MySQL用户的基本说明: 1.1 用户的基本结构MySQL的用户:用户名@主机 ■用户名:16个字符以内■主机:可以是主机名.IP地址.网络地址等主机名:www.111cn.net,localh ...

  3. 第八章| 2. MySQL数据库|数据操作| 权限管理

    1.数据操作 SQL(结构化查询语言),可以操作关系型数据库 通过sql可以创建.修改账号并控制账号权限:  通过sql可以创建.修改数据库.表:  通过sql可以增删改查数据: 可以通过SQL语句中 ...

  4. Spring Security实现RBAC权限管理

    Spring Security实现RBAC权限管理 一.简介 在企业应用中,认证和授权是非常重要的一部分内容,业界最出名的两个框架就是大名鼎鼎的 Shiro和Spring Security.由于Spr ...

  5. 10款最好用的MySQL数据库客户端图形界面管理工具

    MySQL Workbench 该工具由MySQL开发,是一个跨平台的可视化数据库设计工具.它是DBDesigner4项目备受期待的替代者,它是一个本地图形化工具,支持的操作系统包括Windows.L ...

  6. MySQL数据库以及表的管理

    MySQL数据库以及表的管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 今天我们探讨的话题就是如何使用MySQL做开发,我们运维的主要工作不是去开发SQL的,但尽管如此,我们有 ...

  7. MySQL高级学习笔记(二):mysql配置文件、mysql的用户与权限管理、mysql的一些杂项配置

    文章目录 mysql配置文件 二进制日志log-bin 错误日志log-error 数据文件 两系统 Myisam存放方式 innodb存放方式 如何配置 mysql的用户与权限管理 MySQL的用户 ...

  8. ylbtech-权限管理-数据库设计-功能权限管理技术

    ylbtech-DatabaseDesgin:ylbtech-权限管理-数据库设计-功能权限管理技术 DatabaseName:ylb_permission(权限管理-功能权限管理技术)实现 Type ...

  9. spring boot:spring security用mysql实现动态权限管理(spring boot 2.3.3)

    一,动态权限管理的优点和缺点 1,优点: 因为控制权限的数据保存在了mysql或其他存储系统中, 可以动态修改权限控制,无需改动代码和重启应用,  权限变更时灵活方便 2,缺点: 权限的设置需要保存在 ...

随机推荐

  1. Flash 0day(CVE-2018-4878)复现过程

    一.前言介绍 2018年2月1号,Adobe官方发布安全通报(APSA18-01),声明Adobe Flash 28.0.0.137及其之前的版本,存在高危漏洞(CVE-2018-4878). 从Ad ...

  2. Linux实战(6):Centos8上传镜像

    小记 做以下操作之前得准备一个Docker Hub 的账号,然后创建仓库可私有也可公有这得看你自己了,仓库的名称需记住等等会有用. 安装podman podman跟docker使用的命令非常的接近,但 ...

  3. SSM框架整合核心内容

    所需要的jar包及其版本 Spring 版本:4.3.18   tx.aop.beans.core.web.web-mvc.context.expression.jdbc MyBatis:3.4.6 ...

  4. DC 1-3 靶机渗透

    DC-1靶机 端口加内网主机探测,发现192.168.114.146这台主机,并且开放了有22,80,111以及48683这几个端口. 发现是Drupal框架. 进行目录的扫描: 发现admin被禁止 ...

  5. NLP常见任务

    借助BERT论文, 梳理下自然语言处理当前常见的任务. NLP任务 根据判断主题的级别, 将所有的NLP任务分为两种类型: token-level task: token级别的任务. 如完形填空(Cl ...

  6. Spring Boot 项目打成 war 包部署

    Spring Boot 一个非常方便的功能就是支持内置的 Servlet 容器,一般我们部署 Spring Boot 应用时都是打成一个可执行的 Jar 包进行部署.其实 Spring Boot 也是 ...

  7. BurpSuite抓取本地包方法

    本文重点在介绍抓本地包, 而非介绍抓包步骤 Burpsuite配置 默认配置即可 Chrome 浏览器配置 Falcon Proxy扩展程序配置浏览器代理. 需要抓包的网页是个本地搭建的网址, 一般会 ...

  8. RectTransform简析

    UGUI简述   UGUI主要提供了两个能力 UI元素的渲染与适配(其中UI元素的Mesh中的position信息就是通过RectTransform生成的,本文重点) 设备事件的响应与处理(Event ...

  9. GameObject的==的一个坑和一点GameObject的内部构造

    一切都是因为==,才有了这篇博客 目录 测试 结果和分析 总结 测试 先放一段unity的一个普通的脚本 using UnityEngine; public class UnityEngineObje ...

  10. (转载)浏览器 user-agent 字符串的故事

    本文转载自:http://www.cnblogs.com/ifantastic/p/3481231.html. 如有侵权,请联系处理!   你是否好奇标识浏览器身份的User-Agent,为什么每个浏 ...