【springsecurity oauth2授权中心】简单案例跑通流程 P1

news2025/4/22 10:44:39

项目被拆分开,需要一个授权中心使得每个项目都去授权中心登录获取用户权限。而单一项目里权限使用的是spring-security来控制的,每个controller方法上都有 @PreAuthorize("hasAuthority('hello')") 注解来控制权限,想以最小的改动来实现,就学习了一下 spring-boot-starter-oauth2-authorization-server 发现可以满足我的要求,下面是最简单的一个能跑通的案例。

创建项目

创建一个maven项目,新建两个模块

  • authorization-server
  • resource-server

创建好结构如下图
在这里插入图片描述
根目录下的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 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>3.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-security-oauth2-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-security-oauth2-demo</name>
    <description>spring-security-oauth2-demo</description>
    <packaging>pom</packaging>

    <modules>
        <module>authorization-server</module>
        <module>resource-server</module>
    </modules>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <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.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <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>

授权中心

模块 authorization-server 为授权中心 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>spring-security-oauth2-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>authorization-server</artifactId>
    <name>authorization-server</name>
    <description>authorization-server</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 9000
  servlet:
    session:
      cookie:
        name: AuthorizationServer

spring:
  security:
    user:
      name: admin
      password: admin

logging:
  level:
    root: INFO
    org.springframework.web: DEBUG
    org.springframework.security: DEBUG
    org.springframework.security.oauth2: DEBUG

创建配置类 AuthorizationServerConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Configuration
@EnableWebSecurity
public class AuthorizationServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());

        http.exceptionHandling(exceptions ->
                        exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));

        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("user")
                .password("password")
                .authorities("hello", "user")
                .build();

        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("client")
                .clientSecret("secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://localhost:8081/login/oauth2/code/client")
                .scope(OidcScopes.OPENID)
                .scope("user")
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .tokenSettings(TokenSettings.builder()
                        .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
                        .accessTokenTimeToLive(Duration.ofHours(2))
                        .build())
                .build();

        return new InMemoryRegisteredClientRepository(client);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder()
                .issuer("http://localhost:9000")
                .build();
    }

    @Bean
    public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
        return context -> {
            if (context.getTokenType().getValue().equals("access_token")) {
                Collection<? extends GrantedAuthority> authorities = context.getPrincipal().getAuthorities();
                List<String> authorityNames = authorities.stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList());
                context.getClaims().claim("authorities", authorityNames);
            }
        };
    }
}

资源服务器

模块 resource-server 为资源服务器,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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>spring-security-oauth2-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>resource-server</artifactId>
    <name>resource-server</name>
    <description>resource-server</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 8081
  servlet:
    session:
      cookie:
        name: ResourceServer

logging:
  level:
    root: info
    org.springframework.web: debug
    org.springframework.security: debug
    org.springframework.security.oauth2: debug

写三个接口,两个需要不同权限,一个仅需要登录即可访问

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/api/hello")
    @PreAuthorize("hasAuthority('hello')")
    public String hello() {
        return "Hello, you have 'hello' authority!";
    }

    @GetMapping("/api/user")
    @PreAuthorize("hasRole('user')")
    public String user() {
        return "Hello, you have 'user' role!";
    }

    @GetMapping("/api/test")
    public String test() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return "Hello World";
    }
}

添加配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt
                                .jwkSetUri("http://localhost:9000/oauth2/jwks")
                                .jwtAuthenticationConverter(jwtAuthenticationConverter()) // 使用自定义转换器
                        )
                );
        return http.build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities"); // 指定JWT中权限字段名
        grantedAuthoritiesConverter.setAuthorityPrefix(""); // 去掉默认的"SCOPE_"前缀

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

}

测试

先启动授权服务器,再启动资源服务器,然后将参数拼接好在浏览器里直接访问

http://localhost:9000/oauth2/authorize?response_type=code&client_id=client&redirect_uri=http://localhost:8081/login/oauth2/code/client&scope=user

回车后会打开登录页面
在这里插入图片描述
输入授权服务器配置类里配置的用户名和密码进行登录
在这里插入图片描述
用户名和密码验证成功后会跳转到下一个确认授权页面,勾上需要授权的用户信息点击确定
在这里插入图片描述
确定后授权服务器会生成一个code,并通过回调地址传给请求方(我这用的是浏览器,可以在浏览器地址栏里看到)
在这里插入图片描述
拿到code后,打开postman,请求授权服务器的 /oauth2/token 接口用code换access_token
在这里插入图片描述在这里插入图片描述
前面在资源服务器的配置类里将用户登录后的权限信息一块打包进jwt里,所以access_token里现在就有权限信息,可以打开 jwt.io 进行查看
在这里插入图片描述
在postman里请求一个资源服务器的接口 /api/hello
在这里插入图片描述
再请求接口 /api/user 因为用户只有 user权限而没有 user 角色,但/api/user 接口配置的是必须要有 user 角色才能访问,所以响应结果就是403了
在这里插入图片描述

总结

  • 这是一个最简单的使用spring-boot-starter-oauth2-authorization-server实现的授权中心
  • 资源服务器之前如果使用的是springsecurity做的权限控制,几乎不需要修改代码
  • 流程跑通后,后面完善起来就很快了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2340046.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spark—SQL3

连接方式 内嵌Hive&#xff1a; 使用时无需额外操作&#xff0c;但实际生产中很少使用。 外部Hive&#xff1a; 在虚拟机下载相关配置文件&#xff0c;在spark-shell中连接需将hive-site.xml拷贝到conf/目录并修改url、将MySQL驱动copy到jars/目录、把core-site.xml和hdfs-sit…

一文了解相位阵列天线中的真时延

本文要点 真时延是宽带带相位阵列天线的关键元素之一。 真时延透过在整个信号频谱上应用可变相移来消除波束斜视现象。 在相位阵列中使用时延单元或电路板&#xff0c;以提供波束控制和相移。 市场越来越需要更快、更可靠的通讯网络&#xff0c;而宽带通信系统正在努力满…

linux学习 5 正则表达式及通配符

重心应该放在通配符的使用上 正则表达式 正则表达式是用于 文本匹配和替换 的强大工具 介绍两个交互式的网站来学习正则表达式 regexlearn 支持中文 regexone 还有一个在线测试的网址 regex101 基本规则 符号作用示例.匹配任何字符除了换行a.b -> axb/a,b[abc]匹配字符…

基于超启发鲸鱼优化算法的混合神经网络多输入单输出回归预测模型 HHWOA-CNN-LSTM-Attention

基于超启发鲸鱼优化算法的混合神经网络多输入单输出回归预测模型 HHWOA-CNN-LSTM-Attention 随着人工智能技术的飞速发展&#xff0c;回归预测任务在很多领域得到了广泛的应用。尤其在金融、气象、医疗等领域&#xff0c;精确的回归预测模型能够为决策者提供宝贵的参考信息。为…

Android RK356X TVSettings USB调试开关

Android RK356X TVSettings USB调试开关 平台概述操作-打开USB调试实现源码补充说明 平台 RK3568 Android 11 概述 RK3568 是瑞芯微&#xff08;Rockchip&#xff09;推出的一款高性能处理器&#xff0c;支持 USB OTG&#xff08;On-The-Go&#xff09;和 USB Host 功能。US…

消息队列知识点详解

消息队列场景 什么是消息队列 可以把消息队列理解一个使用队列来通信的组件&#xff0c;它的本质是交换机队列的模式&#xff0c;实现发送消息&#xff0c;存储消息&#xff0c;消费消息的过程。 我们通常说的消息队列&#xff0c;MQ其实就是消息中间件&#xff0c;业界中比较…

序列号绑定的SD卡坏了怎么办?

在给SD卡烧录程序的时候&#xff0c;大家发现有的卡是无法烧录的&#xff0c;如&#xff1a;复印机的SD卡不能被复制通常涉及以下几个技术原因&#xff0c;可能与序列号绑定、加密保护或硬件限制有关&#xff1a; 一、我们以复印机的系统卡为例来简单讲述一下 序列号或硬件绑定…

使用SystemWeaver生成SOME/IP ETS ARXML的完整实战指南

使用SystemWeaver生成SOME/IP ETS ARXML的完整实战指南 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;可以分享一下给大家。点击跳转到网站。 https://www.captainbed.cn/ccc 一、SystemWeaver与SOME/IP基础认知 1.1 SystemWe…

Flutter 状态管理 Riverpod

Android Studio版本 Flutter SDK 版本 将依赖项添加到您的应用 flutter pub add flutter_riverpod flutter pub add riverpod_annotation flutter pub add dev:riverpod_generator flutter pub add dev:build_runner flutter pub add dev:custom_lint flutter pub add dev:riv…

【HarmonyOS 5】VisionKit人脸活体检测详解

【HarmonyOS 5】VisionKit人脸活体检测详解 一、VisionKit人脸活体检测是什么&#xff1f; VisionKit是HamronyOS提供的场景化视觉服务工具包。 华为将常见的解决方案&#xff0c;通常需要三方应用使用SDK进行集成。华为以Kit的形式集成在HarmoyOS系统中&#xff0c;方便三方…

Pycharm(九)函数的闭包、装饰器

目录 一、函数参数 二、闭包 三、装饰器 一、函数参数 def func01():print("func01 shows as follows") func01() # 函数名存放的是函数所在空间的地址 print(func01)#<function func01 at 0x0000023BA9FC04A0> func02func01 print(func02)#<function f…

【深度学习】详解矩阵乘法、点积,内积,外积、哈达玛积极其应用|tensor系列02

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a;【深度学习】你真的理解张量了吗&#xff1f;|标量、向量、矩阵、张量的秩|01每日一言&#x1f33c;: “脑袋想不明白的&#xff0c;就用脚想”…

MH2103系列coremark1.0跑分数据和优化,及基于arm2d的优化应用

CoreMark 1.0 介绍 CoreMark 是由 EEMBC&#xff08;Embedded Microprocessor Benchmark Consortium&#xff09;组织于 2009 年推出的一款用于衡量嵌入式系统 CPU 或 MCU 性能的标准基准测试工具。它旨在替代陈旧的 Dhrystone 标准&#xff08;Dhrystone 容易受到各种libc不同…

Flowith AI,解锁下一代「知识交易市场」

前言 最近几周自媒体号都在疯狂推Manus&#xff0c;看了几篇测评后&#xff0c;突然在某个时间节点&#xff0c;在特工的文章下&#xff0c;发现了很小众的Flowith。 被这段评论给心动到&#xff0c;于是先去注册了下账号。一翻探索过后&#xff0c;发现比我想象中要有趣的多&…

SpringBoot企业级开发之【文章分类-新增文章分类】

看一下新增文章的需求&#xff1a; 接口文档&#xff1a; 开发思路&#xff1a; 先在controller下去创建add方法&#xff0c;方法内导入Service类获取add的结果&#xff1b;再在Service接口下去创建add的方法&#xff1b;然后在Service实现类下去实现方法的作用&#xff0c;且导…

【AI News | 20250421】每日AI进展

AI Repos 1、langgraph-mcp-agents 基于LangGraph的AI智能体系统&#xff0c;集成了MCP&#xff0c;使AI助手能访问各种数据源和API。提供了Streamlit网页界面&#xff0c;方便与LangGraph和MCP工具交互。可以通过界面动态添加、删除以及配置MCP工具&#xff0c;无需重启应用&…

牛客 | OJ在线编程常见输入输出练习

1.只有输出 言归正传&#xff0c;本张试卷总共包括18个题目&#xff0c;包括了笔试情况下的各种输入输出。 第一题不需要输入&#xff0c;仅需输出字符串 Hello Nowcoder! 即可通过。 #include <iostream> using namespace std; int main(){string s "Hello Nowco…

python生成动态库在c++中调用

一.Windows下生成动态库.pyd 在setup.py的同目录下使用python setup.py build_ext --inplace 二.在vscode的c中使用.pyd文件&#xff08;动态库&#xff09; 1&#xff09;配置python的环境 python -c "import sys; print(sys.executable)" #确定python安装位置 2…

OpenCV基础函数学习4

【大纲笔记见附件pdf】 目录 一、基于OpenCV的形态学操作 二、基于OpenCV的直方图处理 三、基于OpenCV霍夫变换 四、基于OpenCV模板匹配 一、基于OpenCV的形态学操作 二、基于OpenCV的直方图处理 三、基于OpenCV霍夫变换 四、基于OpenCV模板匹配

Nginx反向代理用自定义Header参数

【啰嗦两句】 也不知道为啥&#xff0c;我仅仅想在Nginx的反向代理中使用自己定义的“x-api-key”做Header参数&#xff0c;却发现会被忽略&#xff0c;网上搜的资料都是说用“proxy_set_header”&#xff0c;却只愿意介绍最基本的几个参数&#xff0c;你懂的&#xff0c;那些资…