Java:SpringBoot整合Spring Security实现认证与授权学习笔记_java spring 入门 授权

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

**开源地址:https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB **

}


发送HTTP请求



GET http://localhost:8080/
Authorization: Basic dXNlcjo2ZjRhMGY5ZS1hY2ZkLTRmNTYtYjIzNy01MTZmYmZjMTk3NGM=


可以获得响应数据



Hello


base64解码之后可以得到用户名和密码



atob(‘dXNlcjo2ZjRhMGY5ZS1hY2ZkLTRmNTYtYjIzNy01MTZmYmZjMTk3NGM=’)

‘user:6f4a0f9e-acfd-4f56-b237-516fbfc1974c’


#### 2、HTTP表单认证


Spring Security的默认认证方式


![在这里插入图片描述](https://img-blog.csdnimg.cn/3548a5b57c9447788f24768b8eed0cde.png)


### 四、Spring Security 用户与认证对象


#### 1、用户对象




| 接口名 | 说明 |
| --- | --- |
| UserDetails | 用户对象 |
| GrantedAuthority | 用户权限 |
| UserDetailsService | 用户对象查询操作 |
| UserDetailsManager | 创建用户、修改用户密码 |


UserDetails 用户对象接口



package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;

public interface UserDetails extends Serializable {
// 获取用户权限信息
Collection<? extends GrantedAuthority> getAuthorities();

// 获取密码
java.lang.String getPassword();

// 获取用户名
java.lang.String getUsername();

// 判断账户是否失效
boolean isAccountNonExpired();

// 判断账户是否锁定
boolean isAccountNonLocked();

// 判断账户凭证信息是否已失效
boolean isCredentialsNonExpired();

// 判断账户是否可用
boolean isEnabled();

}


GrantedAuthority 用户拥有权限接口



package org.springframework.security.core;

import java.io.Serializable;

public interface GrantedAuthority extends Serializable {
// 获取权限信息
String getAuthority();
}


UserDetailsService 用户查询操作



package org.springframework.security.core.userdetails;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public interface UserDetailsService {
// 根据用户名获取用户信息
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}


UserDetailsManager 用户CRUD操作



package org.springframework.security.provisioning;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

public interface UserDetailsManager extends UserDetailsService {

// 创建用户
void createUser(UserDetails user);

// 更新用户
void updateUser(UserDetails user);

// 删除用户
void deleteUser(String username);

// 修改密码
void changePassword(String oldPassword, String newPassword);

// 判断用户是否存在
boolean userExists(String username);

}


#### 2、认证对象




| 接口名 | 说明 |
| --- | --- |
| Authentication | 认证请求详细信息 |
| AuthenticationProvider | 认证的业务执行者 |


Authentication 认证请求详细信息



package org.springframework.security.core;

import java.io.Serializable;
import java.security.Principal;
import java.util.Collection;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder;

public interface Authentication extends Principal, Serializable {

// 安全主体所具有的的权限
Collection<? extends GrantedAuthority> getAuthorities();

// 证明主体有效性的凭证
Object getCredentials();

// 认证请求的明细信息
Object getDetails();

// 主体的标识信息
Object getPrincipal();

// 是否认证通过
boolean isAuthenticated();

// 设置认证结果
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;

}


AuthenticationProvider 认证的业务执行者



package org.springframework.security.authentication;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

public interface AuthenticationProvider {

// 执行认证,返回认证结果
Authentication authenticate(Authentication authentication) throws AuthenticationException;

// 判断是否支持当前的认证对象
boolean supports(Class<?> authentication);

}


### 五、基于MySQL自定义认证过程


#### 1、项目结构



$ tree -I target
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ ├── Application.java
│ │ ├── controller
│ │ │ └── IndexController.java
│ │ ├── entity
│ │ │ └── User.java
│ │ ├── mapper
│ │ │ └── UserMapper.java
│ │ ├── security
│ │ │ ├── SecurityConfiguration.java
│ │ │ └── UserAuthenticationProvider.java
│ │ └── service
│ │ ├── UserService.java
│ │ └── impl
│ │ └── UserServiceImpl.java
│ └── resources
│ ├── application.yml
│ ├── sql
│ │ └── schema.sql
│ ├── static
│ └── templates
└── test
├── http
│ └── IndexController.http
└── java
└── com
└── example
└── demo
└── ApplicationTests.java


#### 2、用户表


默认表结构的SQL路径


spring-security-core-5.7.6.jar!/org/springframework/security/core/userdetails/jdbc/users.ddl



create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(500) not null,
enabled boolean not null
);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);


一般情况下,我们使用自己创建的用户表


schema.sql



– 创建用户表
CREATE TABLE tb\_user (
id int NOT NULL AUTO_INCREMENT COMMENT ‘主键id’,
username varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT ‘用户名’,
password varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT ‘密码’,
nickname varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT ‘昵称’,
enabled tinyint NOT NULL DEFAULT ‘1’ COMMENT ‘账号可用标识’,
PRIMARY KEY (id),
UNIQUE KEY idx\_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT=‘用户表’;

– 添加初始数据
insert into tb\_user values (1, “zhangsan”, “zhangsan”, “张三”, 1);
insert into tb\_user values (2, “lisi”, “lisi”, “李四”, 1);
insert into tb\_user values (3, “wangwu”, “wangwu”, “王五”, 1);


#### 3、依赖


* Spring Security
* MyBatis-Plus
* MySQL8 JDBC
* Lombok


完整依赖


pom.xml



<?xml version="1.0" encoding="UTF-8"?>


4.0.0

org.springframework.boot
spring-boot-starter-parent
2.7.7

<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </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.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

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

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</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>

#### 4、数据库配置


application.yml



server:
port: 8080

DataSource Config

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/data?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456

mybatis-plus:
configuration:
# 开启SQL语句打印
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 自增主键策略
id-type: AUTO


#### 5、SpringBoot基本框架


启动类 Application.java



package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}

}


实体类 User.java



package com.example.demo.entity;

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.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Arrays;
import java.util.Collection;

@Data
@TableName(“tb_user”)
public class User implements UserDetails {
/**
* 主键id
*/
@TableId
private Long id;

/\*\*

* 用户名
*/
private String username;

/\*\*

* 密码
*/
private String password;

/\*\*

* 昵称
*/
private String nickname;

/\*\*

* 账号可用标识
*/
private Integer enabled;

/\*\*

* 获取用户权限信息
*
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority(“ROLE_USER”));
}

/\*\*

* 判断账户是否失效
*
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}

/\*\*

* 判断账户是否锁定
*
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}

/\*\*

* 判断账户凭证信息是否已失效
*
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}

/\*\*

* 判断账户是否可用
*
* @return
*/
@Override
public boolean isEnabled() {
return this.enabled == 1;
}
}


UserMapper.java



package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper {
}


UserService.java



package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.User;

public interface UserService extends IService {
}


UserServiceImpl.java



package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class UserServiceImpl
extends ServiceImpl<UserMapper, User>
implements UserService, UserDetailsService {

/\*\*

* 根据用户名获取用户信息
* @param username
* @return UserDetails
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername, username);

    User user = super.getOne(queryWrapper);

    if(user == null){
        log.error("Access Denied, user not found:" + username);
        throw new UsernameNotFoundException("user not found:" + username);
    }

    return user;
}

}


IndexController.java



package com.example.demo.controller;

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

@RestController
public class IndexController {
@GetMapping(“/hello”)
public String hello(){
return “Hello”;
}
}


#### 6、自动定义Spring Security


SecurityConfiguration.java



package com.example.demo.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfiguration {
/**
* 基于基础认证模式
* @param http
* @return
* @throws Exception
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    // 所有请求都需要认证,认证方式:httpBasic
    http.authorizeHttpRequests((auth) -> {
        auth.anyRequest().authenticated();
    }).httpBasic(Customizer.withDefaults());

    return http.build();
}

}


UserAuthenticationProvider.java



package com.example.demo.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class UserAuthenticationProvider implements AuthenticationProvider {

@Autowired
private UserDetailsService userService;

/\*\*

* 自己实现认证过程
*
* @param authentication
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 从Authentication 对象中获取用户名和密码
String username = authentication.getName();
String password = authentication.getCredentials().toString();

    UserDetails user = userService.loadUserByUsername(username);

    if (password.equals(user.getPassword())) {
        // 密码匹配成功
        log.info("Access Success: " + user);
        return new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
    } else {
        // 密码匹配失败
        log.error("Access Denied: The username or password is wrong!");
        throw new BadCredentialsException("The username or password is wrong!");
    }
}

@Override
public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
}

}


#### 7、接口测试


IndexController.http



不提供认证信息

GET http://localhost:8080/hello

提供错误的认证信息

GET http://localhost:8080/hello
Authorization: Basic dXNlcjo2YzVlMTUyOS1kMTc2LTRkYjItYmZlMy0zZTIzOTNlMjY2MTk=

提供正确的认证信息

GET http://localhost:8080/hello
Authorization: Basic emhhbmdzYW46emhhbmdzYW4=


### 六、使用PasswordEncoder加密密码


PasswordEncoder接口



package org.springframework.security.crypto.password;

public interface PasswordEncoder {

// 对原始密码编码
String encode(CharSequence rawPassword);

// 密码比对
boolean matches(CharSequence rawPassword, String encodedPassword);

// 判断加密密码是否需要再次加密
default boolean upgradeEncoding(String encodedPassword) {
    return false;
}

}


常见的实现类




| 实现类 | 说明 |
| --- | --- |
| NoOpPasswordEncoder | 明文存储,仅用于测试 |
| StandardPasswordEncoder | SHA-256算法(已过期) |
| BCryptPasswordEncoder | bcrypt算法 |
| Pbkdf2PasswordEncoder | Pbkdf2算法 |


Bcrypt算法简介


例如:



package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class BCryptPasswordEncoderTest {

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

}


输出



$2a 10 10 10lKqmIKbEPNDx/RXssgN6POgb8YssAK7pVtMFDosmC8FxozUgQq58K


解释



$是分隔符
2a表示Bcrypt算法版本
10表示算法强度
中间22位表示盐值
中间面的位数表示加密后的文本
总长度60位


使用Bcrypt算法加密密码后的数据
  • 25
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值