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

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

相关文章
|
1月前
|
安全 Java 关系型数据库
springboot整合springsecurity,从数据库中认证
本文介绍了如何在SpringBoot应用中整合Spring Security,并从数据库中进行用户认证的完整步骤,包括依赖配置、数据库表创建、用户实体和仓库接口、用户详情服务类、安全配置类、控制器类以及数据库初始化器的实现。
107 3
springboot整合springsecurity,从数据库中认证
|
27天前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
66 2
|
27天前
|
SQL Java 数据库
Spring Boot与Flyway:数据库版本控制的自动化实践
【10月更文挑战第19天】 在软件开发中,数据库的版本控制是一个至关重要的环节,它确保了数据库结构的一致性和项目的顺利迭代。Spring Boot结合Flyway提供了一种自动化的数据库版本控制解决方案,极大地简化了数据库迁移管理。本文将详细介绍如何使用Spring Boot和Flyway实现数据库版本的自动化控制。
25 2
|
1月前
|
Java 数据库 数据安全/隐私保护
Spring 微服务提示:使用环境变量抽象数据库主机名
Spring 微服务提示:使用环境变量抽象数据库主机名
45 1
|
1月前
|
SQL Java 数据库
Springboot+spring-boot-starter-data-jdbc实现数据库的操作
本文介绍了如何使用Spring Boot的spring-boot-starter-data-jdbc依赖来操作数据库,包括添加依赖、配置数据库信息和编写基于JdbcTemplate的数据访问代码。
61 2
|
2月前
|
XML JavaScript Java
Spring Retry 教程
Spring Retry 是 Spring 提供的用于处理方法重试的库,通过 AOP 提供声明式重试机制,不侵入业务逻辑代码。主要步骤包括:添加依赖、启用重试机制、设置重试策略(如异常类型、重试次数、延迟策略等),并可定义重试失败后的回调方法。适用于因瞬时故障导致的操作失败场景。
Spring Retry 教程
|
2月前
|
前端开发 IDE 数据库连接
ThinkPHP6 模型层的模型属性,表映射关系,以及如何在控制层中使用模型层和模型层中的简单CRUD
本文详细介绍了ThinkPHP6中模型层的使用,包括模型属性设置、表映射关系、以及如何在控制层中使用模型层进行CRUD操作。
ThinkPHP6 模型层的模型属性,表映射关系,以及如何在控制层中使用模型层和模型层中的简单CRUD
|
1月前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
详细介绍实现Java Spring Boot FCM推送教程
97 0
|
2月前
|
数据库 Python
django中数据库外键可以自定义名称吗
django中数据库外键可以自定义名称吗
|
2月前
|
前端开发 数据库 开发者
数据模型(数据库表设计)生成代码
BizWorks ToolKit 插件集成 Mybatis-Plus 代码生成工具,支持从数据库表批量生成代码,简化开发流程。本文详细介绍配置方法及项目示例,包括配置文件格式、生成选项及具体操作步骤,帮助开发者快速实现代码同步更新。配置文件 `.mp.yaml` 支持自定义输出目录、生成组件等,适用于多种项目结构。
47 0
下一篇
无影云桌面