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/**等接口时就会跳转到如下登录界面,登录认证成功后,会跳转到对应的接口访问页面。

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

相关文章
|
3天前
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
3天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
10天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
53 14
|
28天前
|
Java 关系型数据库 数据库连接
使用 Spring Boot 执行数据库操作:全面指南
使用 Spring Boot 执行数据库操作:全面指南
105 1
|
2月前
|
安全 Java 关系型数据库
springboot整合springsecurity,从数据库中认证
本文介绍了如何在SpringBoot应用中整合Spring Security,并从数据库中进行用户认证的完整步骤,包括依赖配置、数据库表创建、用户实体和仓库接口、用户详情服务类、安全配置类、控制器类以及数据库初始化器的实现。
202 3
springboot整合springsecurity,从数据库中认证
|
1月前
|
安全 Java 应用服务中间件
如何将Spring Boot应用程序运行到自定义端口
如何将Spring Boot应用程序运行到自定义端口
50 0
|
2月前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
121 2
|
2月前
|
Java 数据库 数据安全/隐私保护
Spring 微服务提示:使用环境变量抽象数据库主机名
Spring 微服务提示:使用环境变量抽象数据库主机名
50 1
|
2月前
|
SQL Java 数据库
Springboot+spring-boot-starter-data-jdbc实现数据库的操作
本文介绍了如何使用Spring Boot的spring-boot-starter-data-jdbc依赖来操作数据库,包括添加依赖、配置数据库信息和编写基于JdbcTemplate的数据访问代码。
234 2
|
3月前
|
XML JavaScript Java
Spring Retry 教程
Spring Retry 是 Spring 提供的用于处理方法重试的库,通过 AOP 提供声明式重试机制,不侵入业务逻辑代码。主要步骤包括:添加依赖、启用重试机制、设置重试策略(如异常类型、重试次数、延迟策略等),并可定义重试失败后的回调方法。适用于因瞬时故障导致的操作失败场景。
Spring Retry 教程