大数跨境
0
0

Spring全新认证授权服务Spring Authorization Server

Spring全新认证授权服务Spring Authorization Server Spring全家桶实战案例
2024-01-22
0
导读:Spring Authorization Server:构建安全认证与授权系统

环境:SpringBoot3.1.0 + JDK17



1. 简介

Spring 授权服务器(Spring Authorization Server)是一个提供 OAuth 2.1 和 OpenID Connect 1.0 规范及其他相关规范实现的框架。它构建于 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供程序和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。

Spring Authorization Server支持一下功能: 

注意:上面的Authorization Grant一栏,在OAuth2.1中已经删除了下面2个模式

  1. 密码模式(password)

  2. 简化模式(implicit)


2. 环境要求

本文使用的Spring Authorization Server版本是1.1.0,所以对JDK的版本要求是17以上。

Spring Authorization Server构建在Spring Security,所以在学习之前最后是对Spring Security有一定的了解,尤其是从Spring Security5.7开始自定义安全配置由之前的WebSecurityConfigurerAdapter替换为SecurityFilterChain

3. 开发第一个程序

3.1 引入依赖

<dependency>  <groupId>org.springframework.security</groupId>  <artifactId>spring-security-oauth2-authorization-server</artifactId>  <version>1.1.4</version></dependency>


3.2 自定义配置类

在必要的情况下,我们是可以自定义@Bean配置,下面依次介绍每一种自定义Bean的作用。

  • 协议端点的Spring安全过滤器链

    简单说就是OAuth2.1与OIDC相关的接口。

@Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {  OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);  // 启用OpenID Connect 1.0  http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());   http    // 当未从授权端点进行身份验证时,重定向到登录页面    .exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(          new LoginUrlAuthenticationEntryPoint("/login"), new MediaTypeRequestMatcher(MediaType.TEXT_HTML)))    // 接受用户信息 and/or 客户端注册的访问令牌    .oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));  return http.build();}
  • 用于身份验证的过滤器链

    该bean的作用就是在老版本中配置的WebSecurityConfigurerAdapter一样进行用户登录安全验证。

@Bean@Order(2)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {  http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())    // 表单登录处理从授权服务器过滤链重定向到登录页面的操作    .formLogin(Customizer.withDefaults());  return http.build();}
  • 用户加载服务接口

    该接口熟悉Spring Security的都非常清楚,只有一个方法通过用户名加载用户,而在内部是由DaoAuthenticationProvider来调用的。这里使用内存用户。

@Bean public UserDetailsService userDetailsService() {  UserDetails userDetails = User.withUsername("user")      .password("{noop}123123")      .authorities(Arrays.asList(new SimpleGrantedAuthority("p1"), new SimpleGrantedAuthority("p3")))      .roles("USER")      .build();  UserDetails adminUserDetails = User.withUsername("admin")      .password("{noop}123123")      .authorities(Arrays.asList(new SimpleGrantedAuthority("p1"), new SimpleGrantedAuthority("p2"), new SimpleGrantedAuthority("p3")))      .roles("MANAGER")      .build();  return new InMemoryUserDetailsManager(userDetails, adminUserDetails);}
  • 管理客户端

    为每一种客户端提供配置信息;系统默认提供了基于内存的,基于JDBC的。

@Beanpublic RegisteredClientRepository registeredClientRepository() {  RegisteredClient oidcClient = RegisteredClient      .withId("pack001")      // 客户端id      .clientId("123123")      // 客户端密钥      .clientSecret("{noop}666666")      // 客户端支持的方法      .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)      .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)      // 支持的授权类型      .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)      .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)      .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)      // 授权成功后跳转页面      .redirectUri("http://localhost:8881/index.html")      .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())      .build();  return new InMemoryRegisteredClientRepository(oidcClient);}
  • 签名访问令牌

    JWKSource是一个接口,它定义了从JWK(JSON Web Key)集合中获取JWK的方式。JWK是一种表示加密密钥的标准格式,常用于 JWT 签名和加密。JWKSource<SecurityContext> 中的泛型参数SecurityContext通常是一个上下文对象,包含了与安全相关的信息,例如用户身份、角色等。这个类通常用于在处理JWT时提供密钥的来源。例如,当需要验证或生成JWT时,可以使用这个类来获取相应的密钥。

@Beanpublic 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);}// 生成RSA密钥对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;}
  • 解码已签名的访问令牌

@Beanpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {  return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}
  • 配置Spring Authorization Server

    在这里你可以配置每个Endpoint的URI等相关信息。

@Beanpublic AuthorizationServerSettings authorizationServerSettings() {  return AuthorizationServerSettings.builder()    // 授权Endpoint    .authorizationEndpoint("/oauth2/v1/authorize")    // 如果开启了OpenId connect 1.0 并且authorize接口请求的scope中包含openid,那么    // 在请求token接口的时候会通过OAuth2AuthorizationCodeAuthenticationProvider进行生成id_token    // 并且你的token生成必须是jwt类型     .tokenEndpoint("/oauth2/v1/token")    // 自省token的Endpoint    .tokenIntrospectionEndpoint("/oauth2/v1/introspect")    // 撤销token的Endpoint    .tokenRevocationEndpoint("/oauth2/v1/revoke")    .jwkSetEndpoint("/oauth2/v1/jwks")    // OIDC用户信息Endpoint    .oidcUserInfoEndpoint("/userinfo")    .build();}
  • 授权同意服务

    如果不提供该授权同意服务,那么当你认证授权成功以后再跳转到      redirect_uri时,将会出错(能跳转但是没有code值)。默认还提供有基于Jdbc实现的。

@Beanpublic OAuth2AuthorizationConsentService auth2AuthorizationConsentService() {  InMemoryOAuth2AuthorizationConsentService consentService = new InMemoryOAuth2AuthorizationConsentService() ;  OAuth2AuthorizationConsent ac = OAuth2AuthorizationConsent      // 这里的pack001要和上面配置的RegisteredClientRepository id一致      // 用户名也要对应上      .withId("pack001", "user")      // 这里的权限随意      .authority(new SimpleGrantedAuthority("xxoo"))      .build() ;  consentService.save(ac) ;  return consentService ;}


有了上面的配置后,接下来就可以启动服务验证认证码模式及获取token了。

4. 测试认证服务

通过浏览器访问

/oauth2/v1/authorize?client_id=123123&response_type=code&redirect_uri=http://localhost:8080/index.html
授权接口

会跳转到登录页面

输入,上面配置的任意用户即可。成功后会调整到redirect_uri的页面

地址中已经附带了code参数值,接下来通过code值获取token。

访问如下获取token接口

/oauth2/v1/token?grant_type=authorization_code&code=输入刚刚获取到的code&redirect_uri=http://localhost:8080/index.html

获取token接口

成功获取token

整体Spring Authorization Server使用还是比较简单的,核心的类在上面,也做了介绍。下篇将会介绍更多的关于自定义实现的功能。

完毕!!!

​​

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读285
粉丝0
内容832