欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringSecurity实现基于数据库的身份验证

程序员文章站 2022-07-03 11:00:16
...

1. 问题描述

基于数据库MySQL实现用户登录的身份验证,暂时不考虑权限问题;

2.技术选型

SpringBoot+SpringSecurity+SpringData JPA+MySQL;

3.Pom依赖

3.1 springboot版本

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
 </parent>

3.2 maven依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

4.application.yml配置

spring:
#  jpa
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    database: mysql
#  db
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost/yuyufeng?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 2289288246

5.项目结构

SpringSecurity实现基于数据库的身份验证

6.HelloSecurityController实现

package springsecuritydemo1.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloSecurityController {

    @GetMapping
    public String hello(){
        return "Hello Spring Security!";
    }
}

启动项目,访问HelloSecurityController的路由,由于导入了SpringSecurity依赖,会出现默认的身份验证,跳转到 路径: http://localhost:8080/login; 默认用户名是:user,密码会在控制台生成:

Using generated security password: d3f3d4ff-111a-442c-8488-d6dfd4a15475

默认登录页面:
SpringSecurity实现基于数据库的身份验证

7.SpringData jpa实现

7.1 UserInfo实体实现

package springsecuritydemo1.entity;

import javax.persistence.*;

@Entity(name="userInfo")
@Table(name="user_info")
public class UserInfo {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增
    private Long id;
    //用户名
    @Column(name = "username",nullable = false)
    private String username;
    //用户密码
    @Column(name = "password",nullable = false,unique = false)
    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

7.2 UserInfoRepository实现

package springsecuritydemo1.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import springsecuritydemo1.entity.UserInfo;

@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo,Long> {

    UserInfo findByUsername(String username);
}

7.3 UserInfoService接口及UserInfoServiceImpl类实现
(1) UserInfoService接口

package springsecuritydemo1.service;

import springsecuritydemo1.entity.UserInfo;

public interface UserInfoService {

    UserInfo create(UserInfo userInfo);

    UserInfo findByUsername(String username);
}

(2)UserInfoServiceImpl类

package springsecuritydemo1.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import springsecuritydemo1.entity.UserInfo;
import springsecuritydemo1.repository.UserInfoRepository;
import springsecuritydemo1.service.UserInfoService;

@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserInfo create(UserInfo userInfo) {
        //密码加密
        userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));
        return userInfoRepository.save(userInfo);
    }

    @Override
    public UserInfo findByUsername(String username) {
        return userInfoRepository.findByUsername(username);
    }
}

7.4 数据库数据准备

package springsecuritydemo1;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import springsecuritydemo1.entity.UserInfo;
import springsecuritydemo1.service.UserInfoService;

@SpringBootTest
class SpringSecurityDemo1ApplicationTests {

    @Autowired
    private UserInfoService userInfoService;

    @Test
    void contextLoads() {
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setId(1L);
        userInfo1.setUsername("admin");
        userInfo1.setPassword("123456");
        UserInfo userInfoPO1 = userInfoService.create(userInfo1);
        System.out.println(userInfo1);

        UserInfo userInfo2 = new UserInfo();
        userInfo2.setId(2L);
        userInfo2.setUsername("user");
        userInfo2.setPassword("123456");
        UserInfo userInfoPO2 = userInfoService.create(userInfo2);
        System.out.println(userInfo2);
    }
}

不出意外,数据库已存在上述两条数据;

8. MyUserDetailService实现(关键步骤)

创建MyUserDetailService类,实现UserDetailsService接口,重写loadUserByUsername(String s)方法,返回的UserDetails类包含了用户名、密码、权限(暂不考虑),交给SpringSecurity进行身份验证;

package springsecuritydemo1.common.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import springsecuritydemo1.entity.UserInfo;
import springsecuritydemo1.service.UserInfoService;

import java.util.ArrayList;
import java.util.List;

@Component
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        UserInfo userInfo = userInfoService.findByUsername(s);
        if (userInfo == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        //暂时不考虑权限验证,故该权限List为空;
        List<GrantedAuthority> authorities = new ArrayList<>();

        User userDetails = new User(userInfo.getUsername(), userInfo.getPassword(), authorities);
        return userDetails;
    }
}

9.SecurityConfig类实现

创建SecurityConfig类继承WebSecurityConfigurerAdapter类,注入PasswordEncoder;

package springsecuritydemo1.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }
}

10.测试

10.1 测试
访问HelloSecurityController,进入默认登录页面,输入用户名与密码(参照7.4),登录成功,验证成功。