Spring Boot 最新版3.x 集成 OAuth 2.0实现认证授权服务、第三方应用客户端以及资源服务

这篇具有很好参考价值的文章主要介绍了Spring Boot 最新版3.x 集成 OAuth 2.0实现认证授权服务、第三方应用客户端以及资源服务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

Spring Boot 3已经发布一段时间,网上关于Spring Boot 3的资料不是很多,本着对新技术的热情,学习和研究了大量Spring Boot 3新功能和新特性,感兴趣的同学可以参考Spring官方资料全面详细的新功能/新改进介绍

  • Spring版本升级到6.x
  • JDK版本至少17+

新特性有很多,本文主要针对OAuth 2.0的集成,如果快速开发自己的认证授权服务、OAuth客户端以及资源服务

本文开发环境介绍

开发依赖 版本
Spring Boot 3.0.2

开发环境端口说明

新建三个服务,分别对应认证授权服务、OAuth客户端以及资源服务

服务 端口
认证授权服务 8080
OAuth客户端服务 8081
资源服务 8082

认证授权服务

pom.xml依赖

Spring发布了spring-security-oauth2-authorization-server项目,目前最新版是1.0版,pom.xml依赖如下

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
        <version>${spring-security-oauth2-authorization-server.version}</version>
    </dependency>
</dependencies>

新建Oauth2ServerAutoConfiguration类

package com.wen3.oauth.ss.authserver.authconfigure;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
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.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
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.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

@Configuration
public class Oauth2ServerAutoConfiguration {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0
        http
                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -> exceptions
                        .authenticationEntryPoint(
                                new LoginUrlAuthenticationEntryPoint("/login"))
                )
                // Accept access tokens for User Info and/or Client Registration
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .requestMatchers(new AntPathRequestMatcher("/actuator/**"),
                                new AntPathRequestMatcher("/oauth2/**"),
                                new AntPathRequestMatcher("/**/*.json"),
                                new AntPathRequestMatcher("/**/*.html")).permitAll()
                        .anyRequest().authenticated()
                )
                // Form login handles the redirect to the login page from the
                // authorization server filter chain
                .formLogin(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("test")
                .password("test")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("demo-client-id")
                .clientSecret("{noop}demo-client-secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
//                .tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build())
                .redirectUri("http://127.0.0.1:8081/login/oauth2/code/client-id-1")
                .redirectUri("http://127.0.0.1:8081/login/oauth2/code/client-id-2")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope("message.read")
                .scope("message.write")
                .scope("user_info")
                .scope("pull_requests")
                // 登录成功后对scope进行确认授权
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

        return new InMemoryRegisteredClientRepository(registeredClient);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }
}

main函数

@SpringBootApplication
public class OauthServerApplication {

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

yml配置

server:
  port: 8080

第三方应用OAuth客户端

pom.xml依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</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-oauth2-client</artifactId>
    </dependency>
</dependencies>

新建Oauth2ClientAutoConfiguration类

package com.wen3.oauth.ss.authclient.autoconfigure;

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.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class Oauth2ClientAutoConfiguration {

    @Bean
    public SecurityFilterChain authorizationClientSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests()
                .anyRequest().authenticated()
                .and().logout()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
                .and().oauth2Client()
                .and().oauth2Login();

        return http.build();
    }
}

新建OauthClientDemoController类

package com.wen3.oauth.ss.authclient.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class OauthClientDemoController {

    @RequestMapping(path = "/hello")
    public String demo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        log.info("authentication: {}", authentication);

        return "hello";
    }
}

main函数

@SpringBootApplication
public class OauthServerApplication {

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

yml配置

server:
  port: 8081
  servlet:
    session:
      cookie:
        # 需要更换存放sessionId的cookie名字,否则认证服务和客户端的sessionId会相互覆盖
        name: JSESSIONID-2

spring:
  security:
    oauth2:
      client:
        registration:
          client-id-1:
            provider: demo-client-id
            client-id: demo-client-id
            client-secret: demo-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
            #            client-authentication-method: POST
            scope: user_info, pull_requests
            client-name: demo-client-id
          client-id-2:
            provider: demo-client-id2
            client-id: demo-client-id
            client-secret: demo-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
            #            client-authentication-method: POST
            scope: user_info, pull_requests
            client-name: demo-client-id2
        provider:
          demo-client-id:
            authorization-uri: http://127.0.0.1:8080/oauth2/authorize
            token-uri: http://127.0.0.1:8080/oauth2/token
            user-info-uri: http://127.0.0.1:8082/user/info
            user-name-attribute: name
            jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
          demo-client-id2:
            authorization-uri: http://127.0.0.1:8080/oauth2/authorize
            token-uri: http://127.0.0.1:8080/oauth2/token
            user-info-uri: http://127.0.0.1:8082/user/info
            user-name-attribute: name
            jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks

资源服务

pom.xml依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</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-oauth2-resource-server</artifactId>
    </dependency>
</dependencies>

新建ResourceServerAutoConfiguration类

package com.wen3.oauth.ss.resourceserver.autoconfigure;

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.web.SecurityFilterChain;

@Configuration
public class ResourceServerAutoConfiguration {

    @Bean
    public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt();

        return http.build();
    }
}

新建UserController类

package com.wen3.oauth.ss.resourceserver.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class UserController {

    @RequestMapping(path = "/user/info", method = {RequestMethod.GET,RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Map<String, Object> getUser(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "xxx");
        return map;
    }
}

main函数

package com.wen3.oauth.ss.resourceserver;

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

@SpringBootApplication
public class ResourceServerApplication {

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

yml配置

server:
  port: 8082

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks

演示

  • 在浏览器地址栏输入http://127.0.0.1:8081/hello
    springboot集成oauth2.0,Spring Boot,spring,spring boot,java

    因为配置了多个client,会让用户选择用哪个client进行OAuth登录

  • 选择第一个client-id-1
  • 跳转到认证授权服务进行登录
  • 输入用户名test,密码test
    springboot集成oauth2.0,Spring Boot,spring,spring boot,java
  • 登录成功后跳转授权页面
    springboot集成oauth2.0,Spring Boot,spring,spring boot,java
  • 勾选scope确认授权
  • 重定向请求
    springboot集成oauth2.0,Spring Boot,spring,spring boot,java

以上所有页面都是Spring默认的,真实业务开发会自定义这些页面

OAuth客户端openid演示

  • 在浏览器输入http://127.0.0.1:8080/.well-known/openid-configuration
    {"issuer":"http://127.0.0.1:8080","authorization_endpoint":"http://127.0.0.1:8080/oauth2/authorize","token_endpoint":"http://127.0.0.1:8080/oauth2/token","token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"jwks_uri":"http://127.0.0.1:8080/oauth2/jwks","userinfo_endpoint":"http://127.0.0.1:8080/userinfo","response_types_supported":["code"],"grant_types_supported":["authorization_code","client_credentials","refresh_token"],"revocation_endpoint":"http://127.0.0.1:8080/oauth2/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"introspection_endpoint":"http://127.0.0.1:8080/oauth2/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid"]}
    
  • 如果配置了issuer-uri,启动的时候会调用${issuer-uri}/.well-known/openid-configuration获取provider配置信息,如果issuer-uri配置了path也会替换成/.well-known/openid-configuration
  • /.well-known/openid-configuration这个接口获取到的user-info-uri地址是http://127.0.0.1:8080/userinfo 所以会从授权服务获取用户信息
  • 要想让授权服务的/userinfo接口正常返回,则需要在配置registration时,在scope增加openid,同时scope还需要在profileemailaddressphone中增加至少一个,修改后的yml配置如下
server:
  port: 8081
  servlet:
    session:
      cookie:
        # 需要更换存放sessionId的cookie名字,否则认证服务和客户端的sessionId会相互覆盖
        name: JSESSIONID-2

spring:
  security:
    oauth2:
      client:
        registration:
          client-id-1:
            provider: demo-client-id
            client-id: demo-client-id
            client-secret: demo-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
#            client-authentication-method: POST
            scope: openid, profile, user_info, pull_requests
            client-name: demo-client-id
          client-id-2:
            provider: demo-client-id2
            client-id: demo-client-id
            client-secret: demo-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'
#            client-authentication-method: POST
            scope: openid, profile, user_info, pull_requests
            client-name: demo-client-id2
        provider:
          demo-client-id:
            issuer-uri: http://127.0.0.1:8080
          demo-client-id2:
            issuer-uri: http://127.0.0.1:8080
  • 授权页面会有变化
    springboot集成oauth2.0,Spring Boot,spring,spring boot,java

结束

本人经过研读SpringBoot3相关源码,基本上把所有功能都体验了一遍,这篇文章主要是针对最新版的SpringBoot集成OAuth功能进行演示,背后的原理,大家有疑问的可以留言交流。文章来源地址https://www.toymoban.com/news/detail-782092.html

到了这里,关于Spring Boot 最新版3.x 集成 OAuth 2.0实现认证授权服务、第三方应用客户端以及资源服务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Security6 最新版配置该怎么写,该如何实现动态权限管理

    Spring Security 在最近几个版本中配置的写法都有一些变化,很多常见的方法都废弃了,并且将在未来的 Spring Security7 中移除,因此又补充了一些新的内容,重新发一下,供各位使用 Spring Security 的小伙伴们参考。 接下来,我把从 Spring Security5.7 开始(对应 Spring Boot2.7 开始),各

    2024年02月12日
    浏览(45)
  • Jenkins持续集成,在Linux中安装最新版Jenkins(详细)

    去年从6月28日发布的 Jenkins 2.357 和即将发布的 9 月 LTS 版本开始,Jenkins 最低需要 Java 11。 也就是说最新版本的jenkins (2.403)不支持jdk1.8版本了,最低需要jdk1.11 1、安装jdk 以centos 为例,yum安装 如果之前有安装jdk1.8 需先卸载掉 检查当前版本号

    2024年02月12日
    浏览(42)
  • 『Jenkins』最新版Jenkins安装与Git集成—CentOS 7安装的详细教程

    📣读完这篇文章里你能收获到 图文形式安装Jenkins 在Jenkins集成Git并进行的配置 感谢点赞+收藏,避免下次找不到~ Jenkins是一个开源的自动化工具,广泛用于软件开发和持续集成。本文将介绍如何在CentOS 7操作系统上安装Jenkins,并配置其基本设置。 Jenkins是基于Java开发的,最新

    2024年02月09日
    浏览(68)
  • Spring常见面试题55道(附答案2023最新版)

    Spring框架是一个开源的Java平台,它最初由Rod Johnson创建,并在2003年首次公布。它的主要功能是简化Java开发,特别是企业级应用程序的开发。Spring框架的设计哲学是通过提供一系列模块化的组件,帮助开发者创建高性能、易测试、可重用的代码。现在,让我们更深入地了解S

    2023年04月22日
    浏览(84)
  • Spring Cloud面试题及答案(2022最新版)

    最近一个月几乎每天都在面试,最终皇天不负有心人,终于拿到offer了。 整理了一些2022年最新的Spring Cloud面试题及答案,分享给大家~ Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的 集成。Spring cloud Task,一个生命周期短暂的微服务框架

    2024年02月14日
    浏览(46)
  • SpringBoot 集成MyBatis-Plus-Generator(最新版3.5.2版本)自动生成代码(附带集成MyBatis-Plus)

    快速入门 代码生成器配置(新) spring boot 2.3.12.RELEASE mybatis 3.5.2 mybatis plus 3.5.2 mybatis plus generator 3.5.2 mysql 8.0.17 velocity 2.3 hutool 5.8.15 druid 1.2.8 lombok 自带 示例程序选择的技术目前各项技术的稳定版本。 相信大家厌烦重复的造轮子过程,编写与数据库表对应的实体类,接着再进

    2024年02月21日
    浏览(54)
  • 微信小程序新版隐私协议弹窗实现最新版

    2023.08.22更新:【原文连接】 以下指南中涉及的 getPrivacySetting、onNeedPrivacyAuthorization、requirePrivacyAuthorize 等接口目前可以正常接入调试。调试说明: 在 2023年9月15号之前,在 app.json 中配置 __usePrivacyCheck__: true 后,会启用隐私相关功能,如果不配置或者配置为 false 则不会启用。

    2024年02月10日
    浏览(79)
  • VBA实现毫秒级延时(2022最新版)

    要不是年会需要使用PPT来做抽奖,我才不会用这么难用的VBA。 VBA要实现延时功能,大多数教程都会拿2016年ExcelHome里的上古帖子不厌其烦地复制粘贴,然后你复制下来发现根本无法运行。 现在我从头给你讲,到底怎样在VBA中实现毫秒级延时功能。 思路很清晰,分三步走: 1

    2024年02月07日
    浏览(80)
  • Android实现简单的登陆页面(最新版2023详细图文教程

    目录   1.打开android studio软件  2.新建empty activity 3.看个人配置填(finish)  4.左侧找到res-layout(页面布局)  5.先设置页面布局,这里采用线性布局  7.设置头文本    --文本展示标签  8.用户名与密码--可编辑文本标签 9.提交按钮 10.整体代码 LinearLayout xmlns:android=\\\"http://schemas.an

    2024年02月16日
    浏览(64)
  • 使用ChatGPT最新版实现批量写作,打造丰富多彩的聚合文章

    随着人工智能的迅猛发展,ChatGPT最新版作为一种自然语言处理模型,可以为我们提供强大的文本生成能力。在这篇文章中,我们将探讨如何利用ChatGPT最新版来实现批量写作,从而打造丰富多彩的聚合文章。 一、ChatGPT最新版简介 ChatGPT最新版是由OpenAI开发的一种基于大规模预

    2024年02月09日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包