使用JDBCUserDetailsService方式实现身份验证

Posted 流星蝴蝶没有剑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用JDBCUserDetailsService方式实现身份验证相关的知识,希望对你有一定的参考价值。

spring security 两个作用:

  1. 你是谁:who are you?
    专业一点叫: Authentication(认证):
    a. 内存认证
    b. jdbc认证
    c. UserDetailsService认证
    d. ldap 认证
    下面是源码中的四个认证方法

  2. 你能干什么:what are you allwoed to do?

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>wx0725.top</groupId>
    <artifactId>guojihua</artifactId>
    <version>1.0.0</version>

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

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- Spring Boot提供的配置处理器依赖,代码提示 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--        web 开发场景-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--        测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--        热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <!--    静态模板依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


        <!--        IO 操作-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>


        <!--        数据库连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--        阿里巴巴 适配的druid数据源启动器 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <!--        Spring   Data JPA启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--        redis 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

<!--        spring security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

<!--        JDBC 连接数据库的依赖启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>




    </dependencies>


    <!--    maven 打包工具等插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.3.RELEASE</version>
            </plugin>
        </plugins>
    </build>


</project>

1. 内存认证

写一个配置类,自定义内存认证。

package wx0725.top.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;

/**
 * @author WEN
 * @version 1.0
 * @description: Wen Xuan
 * @date 2021/5/7 下午 19:42
 * @link http://wx0725.top
 */
@EnableWebSecurity
// 上面一个注解中包含下面三个注解
//@Import
//@EnableGlobalAuthentication
//@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 
    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception 
//        选择定义密码加密算法
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//        内存认证使用这个密码加密
        authenticationManagerBuilder.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder)
//                注册一个身份,并且密码需要加密
                .withUser("文轩").password(bCryptPasswordEncoder.encode("0725")).roles("common")
                .and()
                .withUser("wenxuan").password(bCryptPasswordEncoder.encode("wenxuan")).roles("vip");
    


新建一个index.html

<html>
    <head>
        <meta charset = "UTF-8">
        <meta name = "viewport"
              content = "width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv = "X-UA-Compatible" content = "ie=edge">
        <title>首页</title>
    </head>
    <body>
        <h1>hello world!</h1>
    </body>
</html>

运行项目
输入:127.0.0.1:8082 也就是直接访问 spring boot 项目地址
此时会自动跳珠到login


可以看到login页面是从127.0.0.1:8082重定向过去的

2. JDBC 认证

需要新建一个安全检测的表格:

客户表:

身份表:

客户对应的身份表

在内存认证的配置类中如下代码:

package wx0725.top.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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 javax.sql.DataSource;

/**
 * @author WEN
 * @version 1.0
 * @description: Wen Xuan
 * @date 2021/5/7 下午 19:42
 * @link http://wx0725.top
 */
@EnableWebSecurity
// 上面一个注解中包含下面三个注解
//@Import
//@EnableGlobalAuthentication
//@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 
    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception 
//        选择定义密码加密算法
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//        内存认证使用这个密码加密
        authenticationManagerBuilder.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder)
//                注册一个身份,并且密码需要加密
                .withUser("user").password(bCryptPasswordEncoder.encode("user")).roles("common")
                .and()
                .withUser("admin").password(bCryptPasswordEncoder.encode("admin")).roles("common");

//        使用JDBC认证
//        查询客户
        String usql = "select username,password,valid from t_customer where username=?";
//        查询客户对应的身份
        String asql = "select c.username, a.authority from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username=?";
        authenticationManagerBuilder.jdbcAuthentication().passwordEncoder(bCryptPasswordEncoder)
                .dataSource(dataSource)
                .usersByUsernameQuery(usql)
                .authoritiesByUsernameQuery(asql);


    

    //    允许忽略静态资源的安全访问
    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/fonts/**");
    



登录成功之后会跳转到首页
这里其实跳到首页是因为第一次输入的时候,输入的是127.0.0.1:8082
如果你输入的是其他的页面,在登录成功之后会跳转到那个页面,并非跳转到首页。

3.UserDetailsService

这一部分代码不少:
其主要实现的是减少数据库压力,通过用户缓存信息,实现验证登录,在登录的时候只查询一次数据库,将其保存在缓存中,下次查询就直接读取缓存。

  1. 新建三个实体类
    分别对应上面的三个表格,但是这里没有用到用户权限id对应的实体类,只在查询的时候用到表格了,所以该表没写对应的实体类
  • Customer.java
  • 保存用户对应的权限
package wx0725.top.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;

/**
 * @author WEN
 * @version 1.0
 * @description: Wen Xuan
 * @date 2021/5/8 下午 13:58
 * @link http://wx0725.top
 *
 * 用户信息
 */
@Entity(name = "t_customer")
public class Customer implements Serializable 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String username;

    private String password;

    private Integer valid;

    @Override
    public String toString() 
        return "Customer" +
                "id=" + id +
                ", username='" + username + '\\'' +
                ", password='" + password + '\\'' +
                ", valid=" + valid +
                '';
    

    public Integer getId() 
        return id;
    

    public void setId(Integer 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;
    

    public Integer getValid() 
        return valid;
    

    public void setValid(Integer valid) 
        this.valid = valid;
    


  • Authority.java
  • 保存用户的基本信息,这里是我自己设置的,没有对应数据库字段

package wx0725.top.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;

/**
 * @author WEN
 * @version 1.0
 * @description: Wen Xuan
 * @date 2021/5/8 下午 14:13
 * @link http://wx0725.top
 * <p>
 * 用户权限
 */

@Entity(name = "t_authority")
public class Authority implements Serializable 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String authority;

    @Override
    public String toString() 
        return "Authority" +
                "id=" + id +
                ", authority='" + authority + '\\'' +
                '';
    

    public Integer getId() 
        return id;
    

    public void setId(Integer id) 
        this.id = id;
    

    public String getAuthority() 
        return authority;
    

    public void setAuthority(String authority) 
        this.authority = authority;
    


  1. 还需对应写两个接口:
  • CustomerRepository.java
  • 通过用户名查询用户信息
package wx0725.top.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import wx0725.top.domain.Customer;

/**
 * @author WEN
 * @version 1.0
 * @description: Wen Xuan
 * @date 2021/5/8 下午 13:56
 * @link http://wx0725.top
 * <p>
 * 用户信息查询接口
 */

public interface CustomerRepository extends JpaRepository<Customer, Integer> 
    //    可以直接使用JPA生成的实现
//    @Query(value = "select c.* from t_customer c where c.username=?1", nativeQuery = true)
    Customer findByUsername(String username);



  • AuthorityRepository.java
  • 通过用户名查询用户对应的权限
  • 需要注意的是查询时需要些 a.* 不然会报错,懵逼,这里没查到
package wx0725.top.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import wx0725.top.domain.Authority;

import java.util.List;

/**
 * @author WEN
 * @version 1.0
 * @description: Wen Xuan
 * @date 2021/5/8 下午 13:57
 * @link http://wx0725.top
 */

public interface AuthorityRepository extends JpaRepository<Authority, Integer> 
//    a.*
    @Query(value = "select a.* from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username=?1", nativeQuery = true)
    List<Authority> findAuthoritiesByUsername(String s);


  1. 接下来就是具体的实现操作,也就是业务层
    对接实体层与控制层
  • CustomerService.java
  • 用来获取用户信息与权限,方便控制层调用
package wx0725.top.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import wx0725.top.domain.Authority;
import wx0725.top.domain如何使用R语言进行卡方检验

卡方检验和趋势卡方检验的区别?如何在SPSS中操作?

请教:SPSS卡方检验的结果分析!! 【重要】

将 catel 与 3rd 方控件一起使用

Python 卡方检验

SPSS做卡方检验是报错了