02_学习springdoc与SpringSecurity配合_使用访问令牌来认证

1 前言

  为了搞懂 springdoc 与 spring security 如何配合,我又学了一遍 spring security 的 JWT 相关的知识。🤣为什么学完就忘呢🤣,也许反反复复是普通人的常态吧,毕竟铁杵磨成针,那得费老大劲儿了~

  上一篇《01_学习springdoc的基本使用》 留了一点“遗憾” —— 没有写 springdoc 与 spring security 如何配合。现在“搞定”它了,我们可以愉快地使用 JWT 的访问令牌 accessToken 请求需要认证的 API 接口了~

2 提纲

  本文主要内容如下:

  • springdoc 的简单配置
  • 在 Spring Security 设置访问 swagger 页面时,不需要认证授权
  • springdoc 与 Spring Security 的配合

3. 准备工作

3.1 maven 依赖

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

<!-- security jwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>${jjwt.version}</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>${jjwt.version}</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>${jjwt.version}</version>
    <scope>runtime</scope>
</dependency>

<!-- spring doc -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>${springdoc.version}</version>
</dependency>
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-security</artifactId>
    <version>${springdoc.version}</version>
</dependency>

3.2 Spring Security 与 JWT

  这一部分需要做到:

  1. 能够使用 Jwts 创建和解析 token;
  2. 能够使用 refreshToken 获取新的 accessToken;
  3. 能够自定义一个 JwtFilter ,从请求头 Authorization: Bearer xxx.xxx.xxx 中拿到 accessToken,然后解析它,构建 UsernamePasswordAuthenticationToken,最后把它放到 SecurityContextHolder 中, 完成认证。

  由于本文的重点不是怎么使用 Spring Security ,而且相关的代码也多,所以上述3点就略过了。如果此时的你感觉无从下手,那我推荐在 慕课网 搜索 spring security 来学习。慕课网的这门课,课很好,🤣奈何我愚笨,学了2遍了,刚刚会了 JWT(估计过一阵子就又忘了😂),对后面的 oauth2 还是懵懵懂懂。路漫漫其修远兮,吾将上下而求索。

  经过努力😁,springdoc 与 oauth2 的结合,也OK了,链接如下 《04_学习springdoc与oauth结合_简述》

4 springdoc 的简单配置

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySpringDocConfig {
    /**
     * 一个自定义的 OpenAPI 对象
     *
     * @return 一个自定义的 OpenAPI 对象
     */
    @Bean
    public OpenAPI customOpenApi() {
        return new OpenAPI()
                .components(new Components()
                        // 设置 spring security jwt accessToken 认证的请求头 Authorization: Bearer xxx.xxx.xxx
                        .addSecuritySchemes("authScheme", new SecurityScheme()
                                .type(SecurityScheme.Type.HTTP)
                                .bearerFormat("JWT")
                                .scheme("bearer")))
                // 设置一些标题什么的
                .info(new Info()
                        .title("学习 Activiti 7 的 API")
                        .version("1.0.0")
                        .description("加油↖(^ω^)↗")
                        .license(new License()
                                .name("Apache 2.0")
                                .url("https://www.apache.org/licenses/LICENSE-2.0")));
    }
}

  效果如下图:

在这里插入图片描述

5 在 Spring Security 设置访问 swagger 页面时,不需要认证授权

import com.example.activiti7learn.security.filter.JwtFilter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
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.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Configuration
@RequiredArgsConstructor
public class MySecurityConfig {
    private final JwtFilter jwtFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .antMatchers("/auth/**").permitAll()
                        .anyRequest().authenticated())
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                // 在普通的用户名密码验证之前, 把 jwt 的过滤器加上
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // 忽略 /error 页面
        return web -> web.ignoring().antMatchers("/error",
                        // 忽略 swagger 接口路径
                        "/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html")
                // 忽略常见的静态资源路径
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }
}

6 springdoc 与 Spring Security 的配合

  在第 4 部分,已经有一个 springdoc 的简单配置,其中有和 JWT 相关的配置,代码片断如下(这个是从 springdoc 官网上学到的):

// 设置 spring security jwt accessToken 认证的请求头 Authorization: Bearer xxx.xxx.xxx
.addSecuritySchemes("authScheme", new SecurityScheme()
        .type(SecurityScheme.Type.HTTP)
        .bearerFormat("JWT")
        .scheme("bearer")))

  有了上面的配置,算是成功第一步了。接下来我们还要给 Controller 的方法加注解,如下:

import com.example.activiti7learn.common.JsonResult;
import com.example.activiti7learn.domain.common.PageInfo;
import com.example.activiti7learn.domain.common.PageReq;
import com.example.activiti7learn.domain.req.def.BpmnXmlReq;
import com.example.activiti7learn.domain.req.def.ProcessDefReq;
import com.example.activiti7learn.domain.resp.def.BpmnXmlResp;
import com.example.activiti7learn.service.ProcessDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.activiti.api.process.model.ProcessDefinition;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequiredArgsConstructor
@Tag(name = "流程定义", description = "流程定义 API")
@ApiResponses(@ApiResponse(responseCode = "200", description = "接口请求成功"))
@RequestMapping(path = "/process-definition", produces = MediaType.APPLICATION_JSON_VALUE)
public class ProcessDefinitionController {
    private final ProcessDefinitionService processDefinitionService;

    // 重点在下面的 @SecurityRequirement, 其 name 就是前面 MySpringDocConfig addSecuritySchemes() 配置的 key
    @Operation(summary = "分页查询流程定义数据", security = @SecurityRequirement(name = "authScheme"))
    @PostMapping("/def-page-data")
    public JsonResult<PageInfo<ProcessDefinition>> findProcessDefinitionPage(@RequestBody PageReq<ProcessDefReq> req) {
        return processDefinitionService.findProcessDefinitionPage(req);
    }
}

  写到这里,🤣不得不吐槽一下,使用 springdoc 之后,代码里面的注解是真的多,这么多圈儿 @ 都快要把我绕晕了。上面最重要的一句是 @Operation(summary = "分页查询流程定义数据", security = @SecurityRequirement(name = "authScheme")) ,authScheme 就是前面 MySpringDocConfig .addSecuritySchemes("authScheme" 的 authScheme。

  大功告成了,是不是觉得有点快,有点突然😁。效果如下图:

在这里插入图片描述

7 验证

7.1 登录一个用户

  首先在登录API里面登录一个用户,如下图:

在这里插入图片描述

  由图可知 accessToken 是 eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzbWl0aCIsImF1dGhvcml0aWVzIjpbIlJPTEVfQUNUSVZJVElfVVNFUiJdLCJpYXQiOjE2NzU4MzM2NjIsImV4cCI6MTY3NTg1MTY2Mn0.aW4ui_kEzrvxxSQbx4XZAfR6UGU-5Q7avyOM4oDnf2IdKB4nvA8Vt0DLo1JwKMtzyvZe_4Zwi6v5s_9HUvTpiQ

7.2 给 springdoc 设置 accessToken

  把 7.1 的 accessToken 设置到 springdoc 中,如下图,粘贴好 accessToken 之后,点 Authorize 按钮就行。

在这里插入图片描述

7.3 请求一个需要认证的接口

  这个时候,再请求一个需要认证的接口,就会发现,它自动加上了请求头 Authorization: Bearer xxx.xxx.xxx,如下 curl 代码所示:

curl -X 'POST' \
  'http://localhost:8081/process-definition/def-page-data' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzbWl0aCIsImF1dGhvcml0aWVzIjpbIlJPTEVfQUNUSVZJVElfVVNFUiJdLCJpYXQiOjE2NzU4MzM2NjIsImV4cCI6MTY3NTg1MTY2Mn0.aW4ui_kEzrvxxSQbx4XZAfR6UGU-5Q7avyOM4oDnf2IdKB4nvA8Vt0DLo1JwKMtzyvZe_4Zwi6v5s_9HUvTpiQ' \
  -H 'Content-Type: application/json' \
  -d '{
  "currentPage": 1,
  "pageSize": 20,
  "req": {
    "processDefinitionId": null,
    "processDefinitionKey": "ProcessRuntimeDefKey",
    "processDefinitionKeys": null
  }
}'

在这里插入图片描述

  其实费了大半天劲,要的就是自动加上请求头 Authorization: Bearer xxx.xxx.xxx 的效果😁

8 结语

  感谢阅读~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值