SpringSecurity整合h2及JPA

SpringSecurity整合h2及JPA

一、pom导入,将需要的maven包导入,初始化环境
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>SecurityLearn</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SecurityLearn</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        //导入h2
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
        //导入jpa
        <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>org.zalando</groupId>
            <artifactId>problem-spring-web</artifactId>
            <version>0.28.0-RC.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
二、配置环境yaml
server:
  port: 8080

//输出配置
logging:
  pattern:
    console: '%clr(%d{E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(${PID}){faint} %clr(---){faint} %clr([%8.15t]){cyan} %clr(%-40.40logger{0}){blue} %clr(:){red} %clr(%m){faint}%n'
  level:
    org.springframework.security: DEBUG
    
//配置database数据源
spring:
  datasource:
    username: sa
    password: root
    url: jdbc:h2:mem:test;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver

  h2:
    console:
      enabled: true
      path: /h2-console
      settings:
        trace: false
        web-allow-others: false
//配置jpa
  jpa:
    database: h2
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: update
    generate-ddl: true
    defer-datasource-initialization: true//这一步不可少,jpa自动创建表,不加这个将会导致data.sql无法找到表
//创建data.sql实现数据自动加载,springboot在初始化的时候会自定义创建表,这里的data.sql会自动导入数据。
insert into users(id, username, `name`, mobile, password_hash, enabled, account_non_expired, account_non_locked, credentials_non_expired, email)
values (1, 'user', 'Zhang San', '13012341234', '{bcrypt}$2a$10$jhS817qUHgOR4uQSoEBRxO58.rZ1dBCmCTjG8PeuQAX4eISf.zowm', 1, 1, 1, 1, 'zhangsan@local.dev'),
       (2, 'old_user', 'Li Si', '13812341234', '{SHA-1}7ce0359f12857f2a90c7de465f40a95f01cb5da9', 1, 1, 1, 1, 'lisi@local.dev');
insert into roles(id, role_name) values (1, 'ROLE_USER'), (2, 'ROLE_ADMIN');
insert into users_roles(user_id, role_id) values (1, 1), (1, 2), (2, 1);
三、创建UserDetails以及UserDetailsService
  • UserDetails是用户登录的角色类,实际上是一个接口,这里我们自定义创建UserDetails作为我们的验证角色。

  • 通常的场景下用户是具备一系列属性约束的,这就是UserDetails。

  • 从数据存储种根据用户名查找到用户,是由UserDetailsService定义的。

//用户user表
package com.example.securitylearn.domain;

import com.example.securitylearn.domain.Role;
import lombok.*;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Set;

@AllArgsConstructor
@NoArgsConstructor
@Data
@With
@Builder
@Entity
@Table(name = "users")
public class User  implements UserDetails,Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String username;
  @Column(name = "password_hash",nullable = false)
  private String password;
  private String mobile;
  private String email;
  private String name;
  @Column(name = "account_non_expired",nullable = false)
  private boolean accountNonExpired;
  @Column(name = "account_non_locked",nullable = false)
  private boolean accountNonLocked;
  @Column(name = "credentials_non_expired",nullable = false)
  private boolean credentialsNonExpired;
  private boolean enabled;
  @ManyToMany
  @Fetch(FetchMode.JOIN)
  @JoinTable(name = "users_roles",
  joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "id")},
  inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")})

  private Set<Role> authorities;

}
//角色权限表
package com.example.securitylearn.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;

import javax.persistence.*;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "roles")
public class Role implements GrantedAuthority , Serializable {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(name = "role_name",nullable = false)
  private String authority;

}
//创建UserRepo接口实现对数据库的查找,这里创建find方法是根据用户名查找user表的数据。
package com.example.securitylearn.repository;
import com.example.securitylearn.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;
@Repository
public interface UserRepo extends JpaRepository<User, Long> {
  Optional<User> findOptionByUsername(String name);
}

//jap针对role表的操作
package com.example.securitylearn.repository;
import com.example.securitylearn.domain.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RoleRepo extends JpaRepository<Role,Long> {
}

//创建自定义的UserDetailsServiceImpl
package com.example.securitylearn.security.userdetail;
import com.example.securitylearn.domain.Role;
import com.example.securitylearn.repository.RoleRepo;
import com.example.securitylearn.repository.UserRepo;
import lombok.RequiredArgsConstructor;
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;
@RequiredArgsConstructor
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
  private final UserRepo userRepo;
  private final RoleRepo roleRepo;
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    return userRepo.findOptionByUsername(username)
      .orElseThrow(()->new UsernameNotFoundException(username+"用户名未找到"));

  }
}
//创建密码自动更新的接口UserDetailsPasswordServiceImpl
package com.example.securitylearn.security.userdetail;

import com.example.securitylearn.repository.UserRepo;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.stereotype.Service;
@RequiredArgsConstructor
@Service
public class UserDetailsPasswordServiceImpl implements UserDetailsPasswordService {
  private final UserRepo userRepo;
  @Override
  public UserDetails updatePassword(UserDetails userDetails, String newPassword) {
    return userRepo.findOptionByUsername(userDetails.getUsername())
      .map(user -> {
          System.out.println("更新了"+newPassword);
          return (UserDetails) userRepo.save(user.withPassword(newPassword));
        }
      ).orElse(userDetails);
  }
}

四、配置Security
package com.example.securitylearn.config;

import com.example.securitylearn.security.userdetail.UserDetailsPasswordServiceImpl;
import com.example.securitylearn.security.userdetail.UserDetailsServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;

import javax.sql.DataSource;
import java.util.Map;

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity(debug = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  private final SecurityProblemSupport securityProblemSupport;
  private final UserDetailsServiceImpl userDetailsService;
  private final UserDetailsPasswordServiceImpl userDetailsPasswordServiceImpl;
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf(AbstractHttpConfigurer::disable)
      .httpBasic(Customizer.withDefaults()) //httpBasic是由http协议定义的最基础的认证方式
      .formLogin(Customizer.withDefaults()) //默认登录
      .authorizeRequests(req -> req.mvcMatchers("/api/**").authenticated());//请求认证,访问api下的所有接口都会需要认证
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService)//配置数据源
      .passwordEncoder(passwordEncoder())//密码解析器,即security通过此密码方式加密
      .userDetailsPasswordManager(userDetailsPasswordServiceImpl);
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/h2-console/**");//配置防止h2-console下的页面无法正常访问,这个网址看内嵌的h2数据库。
  }
    
  //密码加密的方法
  @Bean
  PasswordEncoder passwordEncoder() {
    val idForDefault = "bcrypt";
    val encoders = Map.of(
      idForDefault, new BCryptPasswordEncoder(),
      "SHA-1", new MessageDigestPasswordEncoder("SHA-1")
    );

    return new DelegatingPasswordEncoder(idForDefault, encoders);
  }
}

五、配置成功,运行springboot,打开h2空值面板。

image-20220114013222826

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一延为定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值