Spring Security+OAuth2.0+OIDC1.0


文章目录

  • Spring Security+OAuth2.0+OIDC1.0
  • 前言
  • 一、准备工作
    • 1.Spring Security相关依赖包
    • 2.先看下Authorization Code Grant经典图
    • 2.还是画个结构草图
  • 二、创建对应微服务
    • 1.创建springboot-oauth2-server服务
      • 1.1 OAuth Server的SecurityFilterChain个数
      • 1.2 Protocol Endpoints的SecurityFilterChain链
        • 1.2.1 SecurityFilterChain运行原理
        • 1.2.1 OAuth2AuthorizationServerConfigurer中注册的AuthenticationProvider列表
        • 1.2.2 OAuth Server运行时Filter列表
    • 2.创建springboot-oauth2-resource-server服务
    • 3.创建springboot-oauth2-idp服务

前言

之前学习springboot集成spring security的OAauth2.1和OIDC1.0遇到的问题及成功情况,这里做个笔记下次好查看。


一、准备工作

1.Spring Security相关依赖包

 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version><relativePath/> <!-- lookup parent from repository --></parent>
...<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>0.3.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-resource-server</artifactId></dependency><dependency><groupId>com.nimbusds</groupId><artifactId>oauth2-oidc-sdk</artifactId></dependency>

2.先看下Authorization Code Grant经典图

2.还是画个结构草图

二、创建对应微服务

1.创建springboot-oauth2-server服务

结构如下所示:

核心类MyAuthorizationServerConfig如下:

package com.example.demo.config;
...
@EnableWebSecurity
public class MyAuthorizationServerConfig {@Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
//                // Redirect to the login page when not authenticated from the
//                // authorization endpoint
//        http.exceptionHandling((exceptions) -> exceptions
//                        .authenticationEntryPoint(
//                                new LoginUrlAuthenticationEntryPoint("/login"))
//                );http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);//customized authorizationEndpoint,redirectionEndpoint,tokenEndpoint,userInfoEndpoint
//        http.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userinfo ->
//        userinfo.oidcUserService(oauth2UserService())
//        )
//        .oauth2.tokenEndpoint(...)
//        );//if defined http.exceptionHandling then no need to define http.formLogin(Customizer.withDefaults())return http.formLogin(Customizer.withDefaults()).build();}//for authorization_code purpose@Bean@Order(2)public SecurityFilterChain standardSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).formLogin(Customizer.withDefaults());return http.build();}@Beanpublic RegisteredClientRepository registeredClientRepository() {//authorization_codeRegisteredClient loginClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("login-client").clientSecret("{noop}openid-connect").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).redirectUri("http://127.0.0.1:8080/login/oauth2/code/login-client").redirectUri("http://127.0.0.1:8080/authorized").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE)
//                .userNameAttributeName(IdTokenClaimNames.SUB).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();//client credentialRegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("messaging-client").clientSecret("{noop}secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)/*** curl -X POST \http://127.0.0.1:8080/oauth2/token \-H 'cache-control: no-cache' \-H 'content-type: application/x-www-form-urlencoded' \-H 'postman-token: b8d3bbd1-b796-b2a2-eecf-c37277868eb6' \-d 'grant_type=client_credentials&scope=message%3Aread&client_id=messaging-client&client_secret=secret'*/
//                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).scope("message:read").scope("message:write").build();return new InMemoryRegisteredClientRepository(loginClient, registeredClient);}@Beanpublic JWKSource<SecurityContext> jwkSource(KeyPair keyPair) {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);}@Beanpublic JwtDecoder jwtDecoder(KeyPair keyPair) {return NimbusJwtDecoder.withPublicKey((RSAPublicKey) keyPair.getPublic()).build();}
//    @Bean
//    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {//        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
//    }@Beanpublic ProviderSettings providerSettings() {return ProviderSettings.builder().issuer("http://localhost:9001").build();}@Beanpublic UserDetailsService userDetailsService() {UserDetails userDetails = User.withDefaultPasswordEncoder().username("user").password("123").roles("USER").build();
//      MyUser u = new MyUser(1, "user", "12345");
//      PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
//      UserDetails userDetails = new User(u.getUsername(), passwordEncoder.encode(u.getPassword()), u.getAuthorities());return new InMemoryUserDetailsManager(userDetails);}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)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;}
}

application.yml啥也没配置,可以配置clientid和secret等但是正确的做法是把它们放入到数据库(JdbcRegisteredClientRepository),这里将就用内存(InMemoryRegisteredClientRepository)了

logging.level.root: debug
server:port: 9001

注意点:

  1. 采用官方配置缺了下面

http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

导致resource server获取/userinfo报错

[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 401 : [no body]


看了下原因没有加http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);导致了BearerTokenAuthenticationFilter的缺失,所以当resource server发起最后一步的userinfo请求的时候就会出现HTTP/1.1 401 Unauthorized, 如下:

curl --location --request GET ‘http://localhost:9001/userinfo’
–header ‘Authorization: Bearer eyJraWQiOiIyNDZiZDJiMi05YzZkLTQwYTQtOTU3Yi00YTc1Yjc1MDMwMzgiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiYXVkIjoibG9naW4tY2xpZW50IiwibmJmIjoxNjUzNzk4NDI0LCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIl0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo5MDAxIiwiZXhwIjoxNjUzNzk4NzI0LCJpYXQiOjE2NTM3OTg0MjR9.XI42mxbD3IEzHs4NvLoL5jU3amf_wQazm3AIKbdTB-6x4WD0_dWP9ZrYFUMyu3WaqeQdAwaqrl1D680T-4evjCkjaMSUAfFewg1C4j3wzZ6Fhy3R8gtBn_7qnFwAD8Uz1T9ElZhDVbXTgsK9R55nc93bAl8dDbD0ZTo0BYnp0nUXgRyErDbzylhVISXHgqZz4SYyEIYyOe3ipb7AR2ffGWqutZheqOL0QB4mjVcZNVMqHbDEPM-3d6sLnBFoEFn-RjLSMcvv1ZnBFNVEHdwDs2GI4OV8oonygRKCC6XsmFFmiBrtw_veOQzJuhXWGQmjvzjrfe_I0LN5xmBGjCMmlw’
---------------------------------------respnse-------------------------
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=9E6B72EA9F40647AA576242318C8EB31; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0

  1. 下面这个配置也挺重要的,如果配置了exceptionHandling则可以不用formLogin(Customizer.withDefaults()),如果没有配置exceptionHandling则一定加上formLogin(Customizer.withDefaults())
   @Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
//                // Redirect to the login page when not authenticated from the
//                // authorization endpoint
//        http.exceptionHandling((exceptions) -> exceptions
//                        .authenticationEntryPoint(
//                                new LoginUrlAuthenticationEntryPoint("/login"))
//                );http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);//if defined http.exceptionHandling then no need to define http.formLogin(Customizer.withDefaults())return http.formLogin(Customizer.withDefaults()).build();}

否则/oauth2/authorize会出现401 Unauthorized情况:

http://localhost:9001/oauth2/authorize?response_type=code&client_id=login-client&scope=openid%20profile&state=CczwBMaNiABNPyEeB-r_t1FIL5Rz0d80gaJKJBRC6ho%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/login-client&nonce=gAp_KkNwy-x0iJ5N8Fxr66v6h0H3NtdkALO1WQ6A9gs

原因是当/oauth2/authorize访问oauth server时角色是anonymous,而根据oauth server的SecurityFilterChain中请求会先经过AnonymousAuthenticationFilter处理,之后进入ExceptionTranslationFilter(即出现异常或者401则跳转redirect到/login)再进入FilterSecurityInterceptor(这个filter会发现权限不足报AuthenticationException异常到上个filter,这个异常会被ExceptionTranslationFilter捕获),具体实现:ExceptionTranslationFilter->catch ->DelegatingAuthenticationEntryPoint.commence(…)->LoginUrlAuthenticationEntryPoint.commence(…)即使用LoginUrlAuthenticationEntryPoint的默认重定向策略DefaultRedirectStrategy完成redirect到/login页面(即和自己定义的exceptionHandling里面LoginUrlAuthenticationEntryPoint相同原理),日志如下:

2022-05-29 15:29:23.090 DEBUG 11996 --- [nio-9001-exec-1] o.s.security.web.FilterChainProxy        : Securing GET /oauth2/authorize?response_type=code&client_id=login-client&scope=openid%20profile&state=-_hYJfo50QEI4oOgTqioPKARpSg8hNpblS8GGycu_po%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/login-client&nonce=1cm98p6I4fRtgYEtvTOgph38c-kGyXkCceQ_7_ZI0G0
2022-05-29 15:29:23.094 DEBUG 11996 --- [nio-9001-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-05-29 15:29:23.098 DEBUG 11996 --- [nio-9001-exec-1] org.apache.tomcat.util.http.Parameters   : Set encoding to UTF-8
2022-05-29 15:29:23.098 DEBUG 11996 --- [nio-9001-exec-1] org.apache.tomcat.util.http.Parameters   : Decoding query null UTF-8
2022-05-29 15:29:23.099 DEBUG 11996 --- [nio-9001-exec-1] org.apache.tomcat.util.http.Parameters   : Start processing with input [response_type=code&client_id=login-client&scope=openid%20profile&state=-_hYJfo50QEI4oOgTqioPKARpSg8hNpblS8GGycu_po%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/login-client&nonce=1cm98p6I4fRtgYEtvTOgph38c-kGyXkCceQ_7_ZI0G0]
2022-05-29 15:29:23.110 DEBUG 11996 --- [nio-9001-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2022-05-29 15:29:23.111 DEBUG 11996 --- [nio-9001-exec-1] o.s.s.w.session.SessionManagementFilter  : Request requested invalid session id D2A49325F858222D3CCAAF990B244A05
2022-05-29 15:29:23.119 DEBUG 11996 --- [nio-9001-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Failed to authorize filter invocation [GET /oauth2/authorize?response_type=code&client_id=login-client&scope=openid%20profile&state=-_hYJfo50QEI4oOgTqioPKARpSg8hNpblS8GGycu_po%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/login-client&nonce=1cm98p6I4fRtgYEtvTOgph38c-kGyXkCceQ_7_ZI0G0] with attributes [authenticated]
2022-05-29 15:29:23.205 DEBUG 11996 --- [nio-9001-exec-1] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:9001/oauth2/authorize?response_type=code&client_id=login-client&scope=openid%20profile&state=-_hYJfo50QEI4oOgTqioPKARpSg8hNpblS8GGycu_po%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/login-client&nonce=1cm98p6I4fRtgYEtvTOgph38c-kGyXkCceQ_7_ZI0G0 to session
2022-05-29 15:29:23.205 DEBUG 11996 --- [nio-9001-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using Or [Ant [pattern='/oauth2/token', POST], Ant [pattern='/oauth2/introspect', POST], Ant [pattern='/oauth2/revoke', POST]]
2022-05-29 15:29:23.205 DEBUG 11996 --- [nio-9001-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.HeaderContentNegotiationStrategy@73ccd549, matchingMediaTypes=[application/xhtml+xml, image/*, text/html, text/plain], useEquals=false, ignoredMediaTypes=[*/*]]]
2022-05-29 15:29:23.205 DEBUG 11996 --- [nio-9001-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Match found! Executing org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint@7b2bf745
2022-05-29 15:29:23.206 DEBUG 11996 --- [nio-9001-exec-1] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://localhost:9001/login

1.1 OAuth Server的SecurityFilterChain个数

必须的有至少1个SecurityFilterChain

    @Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);return http.formLogin(Customizer.withDefaults()).build();}

可以自定义多个:

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

定义多了一般没啥用,官方推荐2个就够了,第一个是为了实现Protocol Endpoints 第二个是为了自定义逻辑处理authentication, 我觉得非特殊情况一个SecurityFilterChain就够了,即第一个SecurityFilterChain非常关键在Spring Security OAuth Server中。

1.2 Protocol Endpoints的SecurityFilterChain链

1.2.1 SecurityFilterChain运行原理

当有req来时代码调用FilterChainProxy.doFilter()->doFilterInternal()->inner class VirtualFilterChain.doFilter()->filter chain依次执行,代码如下

@Overrideprivate static final class VirtualFilterChain implements FilterChain {...@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {if (this.currentPosition == this.size) {if (logger.isDebugEnabled()) {logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));}// Deactivate path stripping as we exit the security filter chainthis.firewalledRequest.reset();this.originalChain.doFilter(request, response);return;}this.currentPosition++;Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),this.currentPosition, this.size));}nextFilter.doFilter(request, response, this);}}

1.2.1 OAuth2AuthorizationServerConfigurer中注册的AuthenticationProvider列表

0 = {AnonymousAuthenticationProvider@8092}
1 = {JwtClientAssertionAuthenticationProvider@8093}
2 = {ClientSecretAuthenticationProvider@8094}
3 = {PublicClientAuthenticationProvider@8095}
4 = {OAuth2AuthorizationCodeRequestAuthenticationProvider@8096}
5 = {OAuth2AuthorizationCodeAuthenticationProvider@8097}
6 = {OAuth2RefreshTokenAuthenticationProvider@8098}
7 = {OAuth2ClientCredentialsAuthenticationProvider@8099}
8 = {OAuth2TokenIntrospectionAuthenticationProvider@8100}
9 = {OAuth2TokenRevocationAuthenticationProvider@8101}
10 = {OidcUserInfoAuthenticationProvider@8102}

1.2.2 OAuth Server运行时Filter列表

0 = {DisableEncodeUrlFilter@8051}
1 = {WebAsyncManagerIntegrationFilter@6578}
2 = {SecurityContextPersistenceFilter@8052}
3 = {ProviderContextFilter@8053}
4 = {HeaderWriterFilter@8054}
5 = {CsrfFilter@8055}
6 = {LogoutFilter@8056}
7 = {OAuth2AuthorizationEndpointFilter@8057}
8 = {OidcProviderConfigurationEndpointFilter@8058}
9 = {NimbusJwkSetEndpointFilter@8059}
10 = {OAuth2AuthorizationServerMetadataEndpointFilter@8060}
11 = {OAuth2ClientAuthenticationFilter@8061}
12 = {UsernamePasswordAuthenticationFilter@8062}
13 = {DefaultLoginPageGeneratingFilter@8063}
14 = {DefaultLogoutPageGeneratingFilter@8064}
15 = {BearerTokenAuthenticationFilter@7188}
16 = {RequestCacheAwareFilter@8065}
17 = {SecurityContextHolderAwareRequestFilter@8066}
18 = {AnonymousAuthenticationFilter@8067}
19 = {SessionManagementFilter@8068}
20 = {ExceptionTranslationFilter@8069}
21 = {FilterSecurityInterceptor@7041}
22 = {OAuth2TokenEndpointFilter@8070}
23 = {OAuth2TokenIntrospectionEndpointFilter@8071}
24 = {OAuth2TokenRevocationEndpointFilter@8072}
25 = {OidcUserInfoEndpointFilter@8073}

2.创建springboot-oauth2-resource-server服务

结构如下:

Controller代码:

@Controller
public class Oauth2ClientController {public static final Logger logger = LoggerFactory.getLogger(Oauth2ClientController.class);@GetMapping("/")public ResponseEntity<String> index() {return ResponseEntity.ok("my test");}

ResourceServerApplication启动类:

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

application.yml配置:

debug: true
server:port: 8080
spring:thymeleaf:cache: falsesecurity:oauth2:client:registration:login-client:provider: springclient-id: login-clientclient-secret: openid-connectclient-authentication-method: client_secret_basicauthorization-grant-type: authorization_coderedirect-uri: http://127.0.0.1:8080/login/oauth2/code/login-clientscope: openid,profileclient-name: Springgoogle:client-id: your-app-client-idclient-secret: your-app-client-secretgithub:client-id: xxxxxxxxxxxxxxxxxclient-secret: xxxxxxxxxxxxxxxxxxxxredirect-uri: "{baseUrl}/login/oauth2/code/github"provider:spring:authorization-uri: http://localhost:9001/oauth2/authorizetoken-uri: http://localhost:9001/oauth2/tokenjwk-set-uri: http://localhost:9001/oauth2/jwksuser-info-uri: http://localhost:9001/userinfouser-name-attribute: submvc:log-request-details: truelevel:org.springframework.web.client.RestTemplate: DEBUG

整体配置没太多的特殊地方,需要注意的是user-info-uri是/userinfo,而user-name-attribute可以是下面的字段,如sub.

public interface IdTokenClaimNames {String ISS = "iss";String SUB = "sub";String AUD = "aud";String EXP = "exp";String IAT = "iat";String AUTH_TIME = "auth_time";String NONCE = "nonce";String ACR = "acr";String AMR = "amr";String AZP = "azp";String AT_HASH = "at_hash";String C_HASH = "c_hash";
}

3.创建springboot-oauth2-idp服务

这个服务主要是用来存user的信息,但是目前都是存到springboot-oauth2-server内存里面,所以这里可以暂时不用。后面有了再更新。


参考链接
https://docs.spring.io/spring-security/reference/servlet/oauth2/index.html
https://docs.spring.io/spring-authorization-server/docs/current/reference/html/guides/how-to-userinfo.html#how-to-userinfo
https://github.com/spring-projects/spring-security-samples/blob/5.7.x/servlet
https://openid.net/specs/openid-connect-core-1_0.html
https://datatracker.ietf.org/doc/html/rfc6749#section-4.1

Spring Security 5.x+OAuth2.0+OIDC1.0相关推荐

  1. Spring Security(二):OAuth2协议

    ​ Spring Security(二):OAuth2.0协议 活动地址:CSDN21天学习挑战赛 今天学习与研究了OAuth2.0协议,记录一下,方便后面查阅 OAuth2.0即是一个开放标准,我们 ...

  2. Spring Security系列(11)- Security5.0版本Oauth2开放平台环境搭建

    前言 上篇文档,我们了解了OAuth2.0的相关知识,接下来我们搭建一个自己的Oauth2开放平台. 从流程图中,可以看到,后台需要搭建一个认证服务器,负责用户登录.第三方授权等功能,还需要搭建自己的 ...

  3. 8. Spring Security 5.1之 OAuth 2.0 Login

    1.OAuth 2.0 Login OAuth 2.0登录功能为应用程序提供了使用OAuth 2.0提供程序(例如GitHub)或OpenID Connect 1.0提供程序(例如Google)上的现 ...

  4. Spring Security——简单第三方OAuth2登录自动配置——GitHub登录DEMO

    GitHub OAuth2 APP申请 https://github.com/settings/applications/new Maven <!--Spring Security-->& ...

  5. Spring Boot+Vue/前后端分离/高并发/秒杀实战课程之spring Security快速搭建oauth2 内存版身份认证

    Springboot快速搭建oauth2 内存版身份认证 环境准备 点击[Create New Project]创建一个新的项目 项目环境配置 配置Thymeleaf 搭建oauth2认证,加入两个依 ...

  6. Spring Security(5) 整合OAuth2

    文章目录 一.前言 二.什么是OAuth2? 三.应用场景 四.三部分 五.四种授权模式 1. 授权码模式(authorization code) 2. 简化模式(implicit) 3. 密码模式( ...

  7. (二)spring security:使用 OAuth2 SSO 实现单点登录

    一.前言 也许,我应该延续上一篇"(一)spring security:能做什么?"接着写,比如:如何实现RBAC权限动态控制.后端"验证码"生成与校验.JWT ...

  8. spring security oauth2_SpringBoot2 整合OAuth2实现统一认证

    关于OAuth2不做介绍了,网络太多了. 环境:2.2.11.RELEASE + OAuth2 + Redis redis用来实现token的存储. pom.xml org.springframewo ...

  9. oidc auth2.0_使用Spring Security 5.0和OIDC轻松构建身份验证

    oidc auth2.0 "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和 ...

最新文章

  1. git 代理 git_如何不再害怕GIT
  2. php curl ssr,php curl模拟登陆
  3. pom.xml dependency依赖的研究
  4. Nancy之基于Nancy.Owin的小Demo
  5. 泰勒公式在近似计算中的应用
  6. 小波包8层分解与重构MATLAB代码,谐波小波包分解与重构程序谁有呢?
  7. 假如时光能够倒流, 我会这么学习Java
  8. postman安装后闪退
  9. 【机器视觉】二维码检测(QR 码)
  10. FreeRTOS个人笔记-初谈CM3内核
  11. HTML5APP商业开发实战教程——基于WeX5可视化开发平台
  12. 嵌入式设备的机器码、cpu的id号以及网卡mac地址
  13. 鸿蒙系统敏感应用,鸿蒙系统特性“揭晓”!一次开发灵活使用,生态构建难题被解决?...
  14. java编写连接数据库代码
  15. thinkphp6 框架源码分析
  16. AIR2 Betal版可以下载了flash player10.1可以下载了
  17. ipad1无法安装应用程序_适用于儿童的31种出色iPad应用程序
  18. java随机抽题系统_JAVA程序设计题六:随机抽奖系统
  19. 网站后台管理模板(4个)
  20. 【C语言】警告:“scanf_s”: 没有为格式字符串传递足够的参数

热门文章

  1. nginx 上传文件漏洞_文件上传漏洞,解析漏洞总结
  2. ios和android手机测试,IOS和Android进行手机测试有哪些区别?
  3. 哈夫曼树构造(优先队列)
  4. 高速公路视频监控系统的Bypass 工业以太网交换机特点
  5. python替换list中的元素
  6. 【XLL API 函数】xlAbort
  7. 医疗人工智能系统努力在IT系统上表现良好
  8. ECG ×AI: 机器/深度学习的ECG应用入门(3)
  9. 浅谈计算机图书的翻译——“增值翻译”的几个参考例子
  10. Windows系统中svn服务端下载和搭建