Spring Security系列教程10--基于自定义数据库模型实现授权

简介: 前言在上一个章节中,一一哥 给大家讲解了如何基于默认的数据库模型来实现认证授权,在这种模型里,用户的信息虽然是保存在数据库中的,但是有很多的限制!因为我们必须按照源码规定的方式去建库建表,存在灵活性不足的问题。而我们真正开发时,用户角色等表肯定要根据自己的项目需求来单独设计,所以我们有必要进行用户及角色表的自定义设计。那么在本篇文章中,壹哥 就带各位,结合自己的实际项目需求,进行数据库和表的自定义设计,然后在这个自定义的数据库中实现用户信息的认证与授权工作。一. 核心API源码介绍1. UserDetailsService源码在上一章节中,我给大家介绍了一个JdbcUserDeta

前言

在上一个章节中,一一哥 给大家讲解了如何基于默认的数据库模型来实现认证授权,在这种模型里,用户的信息虽然是保存在数据库中的,但是有很多的限制!因为我们必须按照源码规定的方式去建库建表,存在灵活性不足的问题。而我们真正开发时,用户角色等表肯定要根据自己的项目需求来单独设计,所以我们有必要进行用户及角色表的自定义设计。

那么在本篇文章中,壹哥 就带各位,结合自己的实际项目需求,进行数据库和表的自定义设计,然后在这个自定义的数据库中实现用户信息的认证与授权工作。

一. 核心API源码介绍

1. UserDetailsService源码

在上一章节中,我给大家介绍了一个JdbcUserDetailsManager类,其结构关系如下图所示:

JdbcUserDetailsManager类可以实现基于默认数据库模型的授权认证,但是如果我们想要采用一个更加灵活的方式--基于自定义数据库模型来实现认证授权,那么就需要利用UserDetailsService接口来实现了。

请先跟着壹哥来看看UserDetailsService接口的源码,简单了解一下该类的作用。

publicinterfaceUserDetailsService {
/*** Locates the user based on the username. */UserDetailsloadUserByUsername(Stringusername) throwsUsernameNotFoundException;
}

从源码中可以看出,我们可以利用loadUserByUsername()方法,根据用户名查询出对应的UserDetails信息,那么UserDetails是什么呢?

2. UserDetails源码

publicinterfaceUserDetailsextendsSerializable {
Collection<?extendsGrantedAuthority>getAuthorities();
StringgetPassword();
StringgetUsername();
booleanisAccountNonExpired();
booleanisAccountNonLocked();
booleanisCredentialsNonExpired();
booleanisEnabled();
}

UserDetails的源码中我们了解到,UserDetails其实就是一个包含了User信息的类,其中包含了用户名、密码、角色及账号状态等信息。

利用以上的两个核心API,我们就可以进行基于自定义数据库模型的认证授权工作了,因为不管我们项目中关于认证、授权的数据库结构如何变化,只要我们构造出一个UserDetails类,然后利用UserDetailsService进行用户信息的加载就可以了。

二. 基于自定义数据库模型实现授权

1. 创建数据库

在开始今天的代码之前,请先跟着壹哥来创建一个数据库,并在该库里创建用户角色表,建表脚本如下:

CREATETABLE `users` (`id` bigint(20)NOTNULL AUTO_INCREMENT,        `username` varchar(50)NOTNULL,        `password` varchar(60)NOTNULL,        `enable` tinyint(4)NOTNULL DEFAULT `1`,        `roles` text character set utf8,        PRIMARY KEY (`id`),        KEY `username` (`username`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

该表中包含了用户名、密码、角色、可用状态等信息,然后我们在该表中添加一些测试数据,如下图所示:

2. 创建项目

准备好了数据库之后,我们就继续在之前项目的基础之上,创建一个新的model模块,其基本配置信息和之前一样,具体创建过程略,请参考之前的章节进行项目创建!

3. 创建User实体类

在刚才的模块里,我们先创建一个User实体类,这个用户实体类需要实现  UserDetails 接口,并实现接口中的方法。

/*** 用户操作实体类*/publicclassUserimplementsUserDetails {
privateLongid;
privateStringusername;
privateStringpassword;
privateStringroles;
privatebooleanenable;
privateList<GrantedAuthority>authorities;
publicStringgetRoles() {
returnroles;
    }
publicvoidsetRoles(Stringroles) {
this.roles=roles;
    }
publicLonggetId() {
returnid;
    }
publicvoidsetId(Longid) {
this.id=id;
    }
publicbooleanisEnable() {
returnenable;
    }
publicvoidsetEnable(booleanenable) {
this.enable=enable;
    }
@OverridepublicStringgetUsername() {
returnusername;
    }
publicvoidsetUsername(Stringusername) {
this.username=username;
    }
@OverridepublicbooleanisAccountNonExpired() {
returntrue;
    }
@OverridepublicbooleanisAccountNonLocked() {
returntrue;
    }
@OverridepublicbooleanisCredentialsNonExpired() {
returntrue;
    }
@OverridepublicbooleanisEnabled() {
returnthis.enable;
    }
publicvoidsetAuthorities(List<GrantedAuthority>authorities) {
this.authorities=authorities;
    }
@OverridepublicCollection<?extendsGrantedAuthority>getAuthorities() {
returnthis.authorities;
    }
@OverridepublicStringgetPassword() {
returnpassword;
    }
publicvoidsetPassword(Stringpassword) {
this.password=password;
    }
@Overridepublicbooleanequals(Objectobj) {
returnobjinstanceofUser?this.username.equals(((User)obj).username):false;
    }
@OverridepublicinthashCode() {
returnthis.username.hashCode();
    }
}

核心方法介绍:

  • accountNonExpired、accountNonLocked、credentialsNonExpired、enabled 这四个属性分别用来描述用户的状态,表示账户是否没有过期、账户是否没有被锁定、密码是否没有过期、以及账户是否可用;
  • roles 属性表示用户的角色;
  • getAuthorities 方法返回用户的角色信息,一个用户可能会有多个角色,所以这里返回值是一个集合类型,我们在这个方法中把自己的 Role 角色稍微转化一下即可。

4. 定义Mapper接口

数据模型准备好之后,我们再来定义一个 UserMapper接口,这里我们利用Mybatis进行具体的数据库查询,直接利用注解的方式实现即可。

/*** @Mapper注解,可带可不带,因为有MapperScan扫描.*/@MapperpublicinterfaceUserMapper {
@Select("SELECT * FROM users WHERE username=#{username}")
UserfindByUserName(@Param("username") Stringusername);
}

5. 实现UserDetailsService接口

接在在service层,定义一个UserDetailsService子类,实现UserDetailsService接口,然后实现该接口中的loadUserByUsername()方法。这个方法的参数是用户在登录的时候传入的用户名,然后根据用户名去查询用户信息(查出来之后,系统会自动进行密码比对)。

@ServicepublicclassMyUserDetailsServiceimplementsUserDetailsService {
@AutowiredprivateUserMapperuserMapper;
@OverridepublicUserDetailsloadUserByUsername(Stringusername) throwsUsernameNotFoundException {
// 从数据库尝试读取该用户Useruser=userMapper.findByUserName(username);
// 用户不存在,抛出异常if (user==null) {
thrownewUsernameNotFoundException("用户不存在");
        }
// 将数据库形式的roles解析为UserDetails的权限集// AuthorityUtils.commaSeparatedStringToAuthorityList是Spring Security//提供的用于将逗号隔开的权限集字符串切割成可用权限对象列表的方法// 当然也可以自己实现,如用分号来隔开等,参考generateAuthoritiesuser.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
returnuser;
    }
}

主要要在实现方法中,利用user对象的setAuthorities()方法关联用户的所有角色信息,否则用户登录认证时就没有角色,认证也会失败。

6. 配置SecurityConfig类

编写完上面的UserDetailsService接口后,接下来我们创建SecurityConfig配置类,我们在configure方法中,关联配置自定义的UserService对象。

@EnableWebSecurity(debug=true)
publicclassSecurityConfigextendsWebSecurityConfigurerAdapter {
@Overrideprotectedvoidconfigure(HttpSecurityhttp) throwsException {
http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .hasRole("USER")
                .antMatchers("/visitor/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
/***************************************在数据库中创建用户和角色****************************************************/@AutowiredprivateMyUserDetailsServiceuserDetailsService;
@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth) throwsException {
//关联UserDetailsService对象auth.userDetailsService(userDetailsService)
//暂时不对密码进行加密配置                .passwordEncoder(NoOpPasswordEncoder.getInstance());
    }
}

7. 配置入口类

我们这里因为是结合Mybatis来实现数据库操作的,所以一定要在入口类中,利用@MapperScan注解进行Mapper组件的扫描。

@SpringBootApplication@MapperScan("com.yyg.security.mapper")
publicclassDemo03Application {
publicstaticvoidmain(String[] args) {
SpringApplication.run(Demo03Application.class, args);
    }
}

8. 添加数据库配置

接下来我们在application.yml文件中进行数据库连接的配置,请关联配置自己的数据库信息。

spring:  datasource:    url: jdbc:mysql://localhost:3306/db-security?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
    username: root
    password: syc

至此,本案例的核心代码我就给各位编写完毕啦!

9. 代码结构

现在我们整个项目的代码结构如下,各位可以参考创建自己的项目:

三. 启动项目测试

编写完代码之后,接下来我们就把项目启动起来进行测试,这时候的效果跟基于内存模型的授权实现效果是一样的,具体界面我们不再展示。

在访问/admin/**,/user/**等接口时就会跳转到如下登录界面,登录认证成功后,会跳转到对应的接口访问页面。

好了,我们今天又学会了如何基于自定义的数据库模型进行认证授权了,是不是也很简单?如果你有不明白的地方,记得评论区留言哦!

相关文章
|
2月前
|
人工智能 缓存 自然语言处理
保姆级Spring AI 注解式开发教程,你肯定想不到还能这么玩!
这是一份详尽的 Spring AI 注解式开发教程,涵盖从环境配置到高级功能的全流程。Spring AI 是 Spring 框架中的一个模块,支持 NLP、CV 等 AI 任务。通过注解(如自定义 `@AiPrompt`)与 AOP 切面技术,简化了 AI 服务集成,实现业务逻辑与 AI 基础设施解耦。教程包含创建项目、配置文件、流式响应处理、缓存优化及多任务并行执行等内容,助你快速构建高效、可维护的 AI 应用。
|
4月前
|
存储 关系型数据库 分布式数据库
PolarDB 开源基础教程系列 8 数据库生态
PolarDB是一款开源的云原生分布式数据库,源自阿里云商业产品。为降低使用门槛,PolarDB携手伙伴打造了完整的开源生态,涵盖操作系统、芯片、存储、集成管控、监控、审计、开发者工具、数据同步、超融合计算、ISV软件、开源插件、人才培养、社区合作及大型用户合作等领域。通过这些合作伙伴,PolarDB提供了丰富的功能和服务,支持多种硬件和软件环境,满足不同用户的需求。更多信息请访问[PolarDB开源官方网站](https://openpolardb.com/home)。
197 4
|
5月前
|
关系型数据库 MySQL API
新手教程:数据库操作(使用PDO或MySQLi扩展)
本文为新手介绍如何使用PDO和MySQLi扩展连接与操作MySQL数据库。PDO更现代灵活,支持多种数据库,适合大多数应用;MySQLi提供面向过程和面向对象两种API,适合直接控制数据库操作。教程涵盖安装配置、创建连接、执行查询(查询、插入、更新、删除)及错误处理等内容。希望这篇教程能帮助你快速上手PHP中的数据库操作!
251 32
|
5月前
|
XML 监控 前端开发
Spring Boot中的WebFlux编程模型
Spring WebFlux 是 Spring Framework 5 引入的响应式编程模型,基于 Reactor 框架,支持非阻塞异步编程,适用于高并发和 I/O 密集型应用。本文介绍 WebFlux 的原理、优势及在 Spring Boot 中的应用,包括添加依赖、编写响应式控制器和服务层实现。WebFlux 提供高性能、快速响应和资源节省等优点,适合现代 Web 应用开发。
365 15
|
8月前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
431 2
|
7月前
|
存储 机器学习/深度学习 监控
南大通用GBase 8s数据库onbar基础使用教程
数据备份与恢复是确保数据安全和业务连续性的关键。onbar作为GBase 8s数据库的备份工具,需配合存储管理器使用,通过配置BAR_BSALIB_PATH等参数,实现数据的备份与恢复。本文详细介绍了onbar的配置、备份、恢复及监控流程,帮助数据库管理员构建高效的数据保护方案。
|
3月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
110 0
|
3月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
113 0
|
3月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
56 0