Spring Security整合Mybatis-Plus实现基于数据库的用户认证

在这块,我们使用Spring Security整合springboot,mybatis-plus以及restful等知识,从而来实现基于数据库的自定义用户认证过程。

代码实现思路

1.  准备一张用户表,必需字段有主键id以及账号密码,其他看自己需求进行添加。

2. 搭建好项目环境和配置好mybatis数据源。

3. 先写用户类,主要是通过实现UserDetails来完成,并且记得添加mybatis-plus相关注解进行配置

4. 同样按照mybatis-plus的规定来写mapper类以及service层的实现类

5. 然后写一个类来实现UserDetailsService从而重写里面的 loadUserByUsername方法,该方法是Security认证过程中核心方法之一,该方法的主要目的是通过获取的用户账号来对数据库用户表数据进行用户查询,即将认证的数据依赖到数据库内而不是依赖内存,而对于认证的过程则是依赖于另一个Security部分即AuthenticationManager,主要过程即,我们首先在配置类中对该类进行相关配置,大概就是将UserDetailsService中的loadUserByUsername结果即查询到的用户结果以及密码加密对象一并关联起来并且将他们给传入另一个管理类DaoAuthenticationProvider进行处理之后,因为AuthenticationManager是一个接口,不能直接返回该对象,因此我们需要借助它的实现类ProviderManager,即我们在处理完之后再将其传给ProviderManager之后直接返回是初始化对象即可,在此对于该认证类已经算是配置好了。

6. 接着就到控制类中写登录接口了,我们首先将配置好的AuthenticationManager对象直接注入,然后我们再借助UsernamePasswordAuthenticationToken来接收访问的登录数据,之后将其对象导入AuthenticationManager的Authenticate方法即可获得认证成功后Authentication对象(Authentication对象就是用来存储认证用户数据的),其实就是UserDetails对象,该对象就是认证成功后的用户对象,随后通过转型即可取出,最后对于接口的结果响应则不是很重要。

7. 最后再借助postman来测试下接口看是否存在bug即可。

准备数据表

搭建好项目环境

项目需要导的依赖
<properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
<!--        MVC访问起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
<!--        security起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
<!--        连接MySQL数据库依赖-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
<!--        mybatis-plus起步依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3</version>
        </dependency>
<!--        测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder-jammy-base:latest</builder>
                    </image>
                </configuration>
            </plugin>
        </plugins>
    </build>

整个项目大致目录结构:

该项目需要用到的依赖:

通过框架快速搭建好项目之后进行数据源的配置(application.yml)

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/security?useUnicode=true&&characterEncoding=utf8&&useSSL=false&&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.cj.jdbc.Driver
结果类的设计
package com.boot.result;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//统一响应结果
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private Integer code;//状态码  200-成功  403-认证失败  405-授权失败 400-操作失败
    private String message;//提示信息
    private T data;//响应数据

    //快速返回操作成功响应结果(带响应数据)
    public static <E> Result<E> success(E data) {
        return new Result<>(200, "操作成功", data);
    }

    //快速返回操作成功响应结果
    public static Result success() {
        return new Result(200, "操作成功", null);
    }

    public static Result error(String message) {
        return new Result(400, message, null);
    }
}
实现用户类

因为是使用mybatis-plus,因此记得写上相关配置,数据层亦是

package com.boot.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

@Data
@TableName("user")
public class User implements UserDetails {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;
    private String username;
    private String password;
    private String nickname;

    //因为该项目主要是实现用户认证,因此在这对于权限相关先不管
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
数据层以及业务层

根据mybatis-plus规范来实现数据层以及业务层代码,在这涉及的都是mybatis-plus的知识

@Mapper
public interface UserMapper extends BaseMapper<User> {
}
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}
配置自定义数据源以及认证过程

先写一个业务类来实现UserDetailsService从而重写里面的 loadUserByUsername方法,该方法是Security认证过程中核心方法之一,该方法的主要目的是通过获取的用户账号来对数据库用户表数据进行用户查询,即将认证的数据依赖到数据库内而不是依赖内存,而对于认证的过程则是依赖于另一个Security部分即AuthenticationManager,主要过程即,我们首先在配置类中对该类进行相关配置,大概就是将UserDetailsService中的loadUserByUsername结果即查询到的用户结果以及密码加密对象一并关联起来并且将他们给传入另一个管理类DaoAuthenticationProvider进行处理之后,因为AuthenticationManager是一个接口,不能直接返回该对象,因此我们需要借助它的实现类ProviderManager,即我们在处理完之后再将其传给ProviderManager之后直接返回是初始化对象即可,在此对于该认证类已经算是配置好了。

package com.boot.web;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.boot.entity.User;
import com.boot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class SysUserDetailsService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;
    /*
     * @description: 自定义认证数据源
     * @param: [username]
     **/
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username",username);
        User user = userMapper.selectOne(wrapper);
        if(user==null) {
            throw new UsernameNotFoundException("用户账号错误!!!");
        }
        return user;
    }
}

在配置类写认证配置的同时也要对请求授权等进行配置

package com.boot.config;

import com.boot.web.SysUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultSecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private SysUserDetailsService userDetailsService;

    /*
     * @description: 总配置
     * @date: 2024/4/30 0:25
     * @param: [http]
     **/
    @Bean
    public DefaultSecurityFilterChain securityWebFilterChain(HttpSecurity http) throws Exception {

        //首先对请求访问进行授权配置
        http.authorizeHttpRequests(auth-> auth
                //对于/auth/下的所有路径均可访问
                .requestMatchers("/auth/**").permitAll()
                //任何访问请求均需要进行认证
                .anyRequest().authenticated()
        );

        //关闭跨域漏洞防御
        http.csrf(csrf->csrf.disable());
        return http.build();
    }

    /*
     * @description: 配置认证过程
     * @date: 2024/4/30 0:26
     * @return: org.springframework.security.authentication.AuthenticationManager
     **/
    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
        //因为AuthenticationManager是接口,因此借用其实现类来配置
//        ProviderManager providerManager = new ProviderManager();
        //使用该类来关联自定义认证数据源和密码加密编码对象
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setUserDetailsService(userDetailsService);
        daoProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(daoProvider);
    }


    /*
     * @description: 密码加密编码
     * @date: 2024/4/30 0:25
     * @return: org.springframework.security.crypto.password.PasswordEncoder
     **/
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

登录接口实现
package com.boot.controller;

import com.boot.entity.User;
import com.boot.result.Result;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@RestController
@RequestMapping("/auth")
public class UserController {

    @Autowired
    private AuthenticationManager authenticationManager;

    /*
     * @description: 登录接口
     * @param: [user]
     **/
    @PostMapping("/login")
    public Result login(@RequestBody User user, HttpServletResponse response) {
        //借助UsernamePasswordAuthenticationToken类来进行对登录数据的接收处理
        UsernamePasswordAuthenticationToken
                authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
        //将接收的数据交给认证管理器进行认证处理,并且在这进行登录失败的预处理
        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(authenticationToken);
        }catch (AuthenticationException e){
            e.printStackTrace();
            //在这如果出现异常那么就是系统自动给我们抛的认证失败异常,因此如果需要那么在这做个失败响应即可
            //并且一般在认证失败后会给前端响应一个403的状态码
            response.setStatus(403);
            return new Result(403,"操作失败","用户账号或者密码错误!");
        }
        User u = (User)authenticate.getPrincipal();
        //在这模拟根据用户生成一个token返回给前端
        String token = UUID.randomUUID().toString();
        return Result.success(token);
    }
}
测试阶段

在此代码写完了,然后我们直接启动项目开始测试代码

但是启动之后出现了一个bug(O.o)

然后我便上完找了找资料,发现了问题所在,竟是mybatis-plus起步依赖的版本太低的问题,因此我们只需将其依赖版本换为3.5.5即可

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.5</version>
        </dependency>

再次启动

启动成功没有问题,然后我们到postman中进行接口测试

然后呃呃呃又出现了问题,就是用户账号密码都是对的,但是响应结果不对,虽然后面更改了restful风格,然后我找了找,发现问题出现在数据中,因为在数据库中一开始设定的用户密码是123456,并未经过加密,然而在security认证过程中对于用户密码的匹配是将数据密码进过加密后再进行匹配的,因此我们在这得手动将数据库中的用户密码给加密,因此我就快速给写了个测试方法来给它搞了加密后的密码并更新上

    @Test
    public void test() {
        BCryptPasswordEncoder en = new BCryptPasswordEncoder();
        System.out.println(en.encode("123456"));
    }

然后再次进行测试

测试成功!没有问题!然后再测试认证失败的

哦耶,都没有问题!好了,该项目到这也就差不多结束了,以上就是对于本次项目的实现过程,过程中用到比较多的技术支持以及源码思路参考,希望能给读者对于Security的学习提供相关帮助,后面我会慢慢持续更新系列知识,感兴趣的可以收藏加关注,谢谢支持

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Security 是一个开源的安全框架,用于保护基于Spring框架构建的应用程序。MyBatis-Plus 是基于 MyBatis 的增强工具,用于简化 MyBatis 操作和提高开发效率。 将 Spring SecurityMyBatis-Plus 整合主要包括以下几个步骤: 1. 首先,在 Spring Boot 项目的 pom.xml 文件中添加 Spring SecurityMyBatis-Plus 的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>latest version</version> </dependency> ``` 2. 接下来,在项目的配置文件 application.properties 或 application.yml 中配置数据库连接和 MyBatis-Plus 的相关配置。 3. 创建一个用户实体类和对应的 Mapper 接口,使用 MyBatis-Plus 提供的注解和方法完成数据访问操作。 4. 创建一个自定义的 UserDetailsService 实现类,用于从数据库中获取用户信息,实现 loadUserByUsername 方法。 5. 创建一个自定义的 PasswordEncoder 实现类,用于加密和校验用户密码。 6. 配置 Spring Security,创建一个继承自 WebSecurityConfigurerAdapter 的类,并重写 configure 方法,设置用户认证规则、登录配置和访问控制规则。 7. 在 Spring Boot 启动类上使用 @MapperScan 注解,扫描 Mapper 接口。 通过以上步骤,就可以实现 Spring SecurityMyBatis-Plus整合。在实际应用中,还可以根据具体需求进行一些其他配置和扩展,例如添加角色权限控制、自定义登录页面和处理逻辑等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值