广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >Spring Boot 最新版3.x 集成 OAuth 2.0实现认证授权服务、第三方应用客户端以及资源服务
  • 335
分享到

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

springspringbootjava 2023-08-23 12:08:29 335人浏览 八月长安
摘要

目录 前言本文开发环境介绍开发环境端口说明认证授权服务pom.xml依赖新建Oauth2ServerAutoConfiguration类main函数yml配置 第三方应用OAuth客户端pom.xml依赖新建Oauth2Clie

前言

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

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

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

本文开发环境介绍

开发依赖版本
Spring Boot3.0.2

开发环境端口说明

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

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

认证授权服务

pom.xml依赖

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

<dependencies>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-WEBartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-securityartifactId>    dependency>    <dependency>        <groupId>org.springframework.securitygroupId>        <artifactId>spring-security-oauth2-authorization-serverartifactId>        <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;@Configurationpublic 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*.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函数

@SpringBootApplicationpublic 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.bootgroupId>        <artifactId>spring-boot-starter-webartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-securityartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-oauth2-clientartifactId>    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;@Configurationpublic 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@RestControllerpublic class OauthClientDemoController {    @RequestMapping(path = "/hello")    public String demo() {        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();        log.info("authentication: {}", authentication);        return "hello";    }}

main函数

@SpringBootApplicationpublic 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-2spring:  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.bootgroupId>        <artifactId>spring-boot-starter-webartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-securityartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-oauth2-resource-serverartifactId>    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;@Configurationpublic 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;@RestControllerpublic 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;@SpringBootApplicationpublic class ResourceServerApplication {    public static void main(String[] args) {        SpringApplication.run(ResourceServerApplication.class, args);    }}

yml配置

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

演示

  • 在浏览器地址栏输入http://127.0.0.1:8081/hello
    在这里插入图片描述

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

  • 选择第一个client-id-1
  • 跳转到认证授权服务进行登录
  • 输入用户名test,密码test
    在这里插入图片描述
  • 登录成功后跳转授权页面
    在这里插入图片描述
  • 勾选scope确认授权
  • 重定向请求
    在这里插入图片描述

以上所有页面都是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-2spring:  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
  • 授权页面会有变化
    在这里插入图片描述

结束

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

来源地址:https://blog.csdn.net/friendlytkyj/article/details/128889875

--结束END--

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

本文链接: https://www.lsjlt.com/news/378596.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作