oAuth2+JWT+Security实现权限认证体系(下)

简介: oAuth2+JWT+Security实现权限认证体系

在上边类文件中,构建了JWT所需的所有Spring @ Beans。最重要的@Bean是:JwtAccessTokenConverter,JwtTokenStore和DefaultTokenServices。

  • JwtAccessTokenConverter使用自签名证书对生成的令牌进行签名。
  • JwtTokenStore实现仅从令牌本身读取数据。并不是真正的商店,因为它从不持久化任何东西,并且使用JwtAccessTokenConverter生成和读取令牌。
  • DefaultTokenServices使用TokenStore来保存令牌。

配置文件中配置信息

security:
jwt:
  key-store: classpath:keystore.jks
  key-store-password: letmein
  key-pair-alias: mytestkey
  key-pair-password: changeme

资源服务器配置

资源服务器托管HTTP资源,该资源可以是文档,照片或其他内容。

依赖
<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.oauth.boot</groupId>
          <artifactId>spring-security-oauth2-autoconfigure</artifactId>
          <version>2.1.2.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-configuration-processor</artifactId>
          <optional>true</optional>
      </dependency>
      <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>2.6</version>
      </dependency>                
</dependencies>
定义资源保护

下面的代码定义了endpoint /me,/me返回了Principal对象,并且要求经过身份验证的用户具有ROLE USER授予权限才能访问。

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@RestController
@RequestMapping("/me")
public class UserController {
   @GetMapping
   @PreAuthorize("hasRole('ROLE_USER')")
   public ResponseEntity<Principal> get(final Principal principal) {
       return ResponseEntity.ok(principal);
  }
}

@PreAuthorize注解在执行代码之前验证用户是否具有给定角色,以使其起作用,能够启动该注解,需要进行配置

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration {
}
资源服务器配置

要解码JWT令牌,必须使用授权服务器上使用的自签名证书中的公钥对令牌进行签名,为此,我们首先创建一个@ConfigurationProperties类以绑定配置属性。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
@ConfigurationProperties("security")
public class SecurityProperties {
   private JwtProperties jwt;
   public JwtProperties getJwt() {
       return jwt;
  }
   public void setJwt(JwtProperties jwt) {
       this.jwt = jwt;
  }
   public static class JwtProperties {
       private Resource publicKey;
       public Resource getPublicKey() {
           return publicKey;
      }
       public void setPublicKey(Resource publicKey) {
           this.publicKey = publicKey;
      }
  }
}

使用以下命令从生成的JKS导出公钥:

$ keytool -list -rfc --keystore keystore.jks | openssl x509 -inform pem -pubkey -noout

生成如下示例的代码块

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmWI2jtKwvf0W1hdMdajc
h+mFx9FZe3CZnKNvT/d0+2O6V1Pgkz7L2FcQx2uoV7gHgk5mmb2MZUsy/rDKj0dM
fLzyXqBcCRxD6avALwu8AAiGRxe2dl8HqIHyo7P4R1nUaea1WCZB/i7AxZNAQtcC
cSvMvF2t33p3vYXY6SqMucMD4yHOTXexoWhzwRqjyyC8I8uCYJ+xIfQvaK9Q1RzK
Rj99IRa1qyNgdeHjkwW9v2Fd4O/Ln1Tzfnk/dMLqxaNsXPw37nw+OUhycFDPPQF/
H4Q4+UDJ3ATf5Z2yQKkUQlD45OO2mIXjkWprAmOCi76dLB2yzhCX/plGJwcgb8XH
EQIDAQAB
-----END PUBLIC KEY-----

将其复制到public.txt文件并将其放置在/ src / main / resources中,然后配置指向该文件的application.yml:

security:
jwt:
  public-key: classpath:public.txt

增加配置内容

import org.apache.commons.io.IOUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import java.io.IOException;
import static java.nio.charset.StandardCharsets.UTF_8;
@Configuration
@EnableResourceServer
@EnableConfigurationProperties(SecurityProperties.class)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
   private static final String ROOT_PATTERN = "/**";
   private final SecurityProperties securityProperties;
   private TokenStore tokenStore;
   public ResourceServerConfiguration(final SecurityProperties securityProperties) {
       this.securityProperties = securityProperties;
  }
   @Override
   public void configure(final ResourceServerSecurityConfigurer resources) {
       resources.tokenStore(tokenStore());
  }
   @Override
   public void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests()
              .antMatchers(HttpMethod.GET, ROOT_PATTERN).access("#oauth2.hasScope('read')")
              .antMatchers(HttpMethod.POST, ROOT_PATTERN).access("#oauth2.hasScope('write')")
              .antMatchers(HttpMethod.PATCH, ROOT_PATTERN).access("#oauth2.hasScope('write')")
              .antMatchers(HttpMethod.PUT, ROOT_PATTERN).access("#oauth2.hasScope('write')")
              .antMatchers(HttpMethod.DELETE, ROOT_PATTERN).access("#oauth2.hasScope('write')");
  }
   @Bean
   public DefaultTokenServices tokenServices(final TokenStore tokenStore) {
       DefaultTokenServices tokenServices = new DefaultTokenServices();
       tokenServices.setTokenStore(tokenStore);
       return tokenServices;
  }
   @Bean
   public TokenStore tokenStore() {
       if (tokenStore == null) {
           tokenStore = new JwtTokenStore(jwtAccessTokenConverter());
      }
       return tokenStore;
  }
   @Bean
   public JwtAccessTokenConverter jwtAccessTokenConverter() {
       JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
       converter.setVerifierKey(getPublicKeyAsString());
       return converter;
  }
   private String getPublicKeyAsString() {
       try {
           return IOUtils.toString(securityProperties.getJwt().getPublicKey().getInputStream(), UTF_8);
      } catch (IOException e) {
           throw new RuntimeException(e);
      }
  }
}

此配置的重要部分是三个@Bean:JwtAccessTokenConverter,TokenStore和DefaultTokenServices:

  • JwtAccessTokenConverter使用JKS公钥。
  • JwtTokenStore使用JwtAccessTokenConverter读取令牌。
  • DefaultTokenServices使用JwtTokenStore来保留令牌。

集成测试

为了一起测试,我们需要同时启动Authorization Server和Resource Server,在我的设置中它将分别在9000端口和9100端口上运行。

$ curl -u clientId:secret -X POST localhost:9000/oauth/token\?grant_type=password\&username=user\&password=pass
{
 "access_token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDgxODk0NDUsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjFjYWQ3MTktZTkwMS00Njk5LTlhOWEtYTIwYzk2NDM5NjAzIiwiY2xpZW50X2lkIjoiY2xpZW50SWQiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.LkQ3KAj2kPY7yKmwXlhIFaHtt-31mJGWPb-_VpC8PWo9IBUpZQxg76WpahBJjet6O1ICx8b5Ab2CxH7ErTl0tL1jk5VZ_kp66E9E7bUQn-C09CY0fqxAan3pzpGrJsUvcR4pzyzLoRCuAqVRF5K2mdDQUZ8NaP0oXeVRuxyRdgjwMAkQGHpFC_Fk-7Hbsq2Y0GikD0UdkaH2Ey_vVyKy5aj3NrAZs62KFvQfSbifxd4uBHzUJSkiFE2Cx3u1xKs3W2q8MladwMwlQmWJROH6lDjQiybUZOEhJaktxQYGAinScnm11-9WOdaqohcr65PAQt48__rMRi0TUgvsxpz6ow",
 "token_type" : "bearer",
 "refresh_token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImIxY2FkNzE5LWU5MDEtNDY5OS05YTlhLWEyMGM5NjQzOTYwMyIsImV4cCI6MTU1MDc4MTE0NSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6Ijg2OWFjZjM2LTJiODAtNGY5Ni04MzUwLTA5NTgyMzE3NTAzMCIsImNsaWVudF9pZCI6ImNsaWVudElkIn0.TDQwUNb627-f0-Cjn1vWZXFpzZSGpeKZq85ivA9zY_atOXM2WfjOxTLE6phnNLevjLSNAGrx1skm_sx6leQlrrmDi36nwiR7lvhv8xMbn1DkF5KaoWPhldW7GHsSIiauMu_cJ5Kmq89ZOEOlxYoXlLwfWYo75ISkKNYqko98yDogGrRAJxtc1aKIBLypLchhoCf8w43efd11itwvBdaLIb5ACfN30kztUqQtbeL8voQP6tOsRZbCgbOOKMTulOCRyBvaora4GJDV2qdvXdCUT-kORKDj9liqt2ae7OJzb2FuuXCGqBUrxYYK-H-wdwh7XFkXVe74Lev9YDUbyEmDHg",
 "expires_in" : 299,
 "scope" : "read write",
 "jti" : "b1cad719-e901-4699-9a9a-a20c96439603"
}
访问资源

现在您已经生成了令牌,复制访问令牌并将其添加到授权HTTP标头上的请求中,例如:

curl localhost:9100/me -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDgxODk0NDUsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjFjYWQ3MTktZTkwMS00Njk5LTlhOWEtYTIwYzk2NDM5NjAzIiwiY2xpZW50X2lkIjoiY2xpZW50SWQiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.LkQ3KAj2kPY7yKmwXlhIFaHtt-31mJGWPb-_VpC8PWo9IBUpZQxg76WpahBJjet6O1ICx8b5Ab2CxH7ErTl0tL1jk5VZ_kp66E9E7bUQn-C09CY0fqxAan3pzpGrJsUvcR4pzyzLoRCuAqVRF5K2mdDQUZ8NaP0oXeVRuxyRdgjwMAkQGHpFC_Fk-7Hbsq2Y0GikD0UdkaH2Ey_vVyKy5aj3NrAZs62KFvQfSbifxd4uBHzUJSkiFE2Cx3u1xKs3W2q8MladwMwlQmWJROH6lDjQiybUZOEhJaktxQYGAinScnm11-9WOdaqohcr65PAQt48__rMRi0TUgvsxpz6ow"
{
 "authorities" : [ {
   "authority" : "ROLE_GUEST"
} ],
 "details" : {
   "remoteAddress" : "127.0.0.1",
   "sessionId" : null,
   "tokenValue" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDgyMzcxNDEsInVzZXJfbmFtZSI6Imd1ZXN0IiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9HVUVTVCJdLCJqdGkiOiIzNDk1ODE1MC0wOGJkLTQwMDYtYmNhMC1lM2RkYjAxMGU2NjUiLCJjbGllbnRfaWQiOiJjbGllbnRJZCIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.WUwAh-aKgh_Bqk-a9ijw67EI6H8gFrb3D_WdwlEcITskIybhacHjT6E7cUXjdBT7GCRvvJ-yxzFJIQyI6y0t61SInpqVG2GlAwtTxR5reG0e4ZtcKoq2rbQghK8hWenGplGT31kjDY78zZv-WqCAc0-MM4cC06fTXFzdhsdueY789lCasSD4WMMC6bWbN098lHF96rMpCdlW13EalrPgcKeuvZtUBrC8ntL8Bg3LRMcU1bFKTRAwlVxw1aYyqeEN4NSxkiSgQod2dltA-b3c15L-fXoOWNGnPB68hqgK48ymuemRQTSg3eKmHFAQdDL6pxQ8_D_ZWAL3QhsKQVGDKg",
   "tokenType" : "Bearer",
   "decodedDetails" : null
},
 "authenticated" : true,
 "userAuthentication" : {
   "authorities" : [ {
     "authority" : "ROLE_GUEST"
  } ],
   "details" : null,
   "authenticated" : true,
   "principal" : "guest",
   "credentials" : "N/A",
   "name" : "guest"
},
 "credentials" : "",
 "principal" : "guest",
 "clientOnly" : false,
 "oauth2Request" : {
   "clientId" : "clientId",
   "scope" : [ "read", "write" ],
   "requestParameters" : {
     "client_id" : "clientId"
  },
   "resourceIds" : [ ],
   "authorities" : [ ],
   "approved" : true,
   "refresh" : false,
   "redirectUri" : null,
   "responseTypes" : [ ],
   "extensions" : { },
   "grantType" : null,
   "refreshTokenRequest" : null
},
 "name" : "guest"
}

通过示例,演示了配合使用。

目录
相关文章
|
3月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
249 0
|
1月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
【10月更文挑战第2天】在当今互联网世界中,数据安全与隐私保护日益重要。Python 作为广泛应用于 Web 开发的语言,其认证机制也不断进化。OAuth 2.0 和 JSON Web Tokens (JWT) 成为当前最热门的安全认证方案,不仅保障数据安全传输,还简化了用户认证流程。本文将介绍 Python 如何结合 OAuth 2.0 和 JWT 打造安全高效的认证体系。
39 3
|
20天前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
在当今互联网世界中,数据安全和隐私保护至关重要。Python 作为 Web 开发的主流语言,其认证机制也在不断进步。OAuth 2.0 和 JSON Web Tokens (JWT) 是当前最热门的安全认证方案,不仅保障数据安全传输,还简化用户认证流程。本文介绍如何在 Python 中结合 OAuth 2.0 和 JWT,打造一套既安全又高效的认证体系。通过 Flask-HTTPAuth 和 PyJWT 等库,实现授权和验证功能,确保每次请求的安全性和便捷性。
32 3
|
28天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
2月前
|
安全 Java 数据安全/隐私保护
|
2月前
|
JSON 安全 数据安全/隐私保护
Python 安全性大揭秘:OAuth 与 JWT,不只是认证,更是信任的传递
【9月更文挑战第4天】在数字化时代,确保应用安全至关重要。Python 作为广泛使用的编程语言,提供了强大的安全认证工具,如 OAuth 和 JWT。OAuth 是一种授权框架,允许第三方应用在有限权限下访问用户资源;JWT 则是一种自包含的数据传输格式,用于安全地传递声明。通过合理配置和使用这些技术,可以有效提升应用安全性,保障用户数据安全。正确管理和定期更新密钥、严格测试 JWT 的生成与验证等最佳实践,对于构建安全可靠的应用至关重要。不断学习新威胁,是维护应用安全的永恒课题。
53 2
|
3月前
|
JSON 安全 数据安全/隐私保护
Python 安全性大揭秘:OAuth 与 JWT,不只是认证,更是信任的传递
【8月更文挑战第6天】在数字化时代,Python 通过 OAuth 和 JWT 筑牢应用安全防线。OAuth 是一种授权框架,允许第三方应用在用户授权下安全访问资源;JWT 则是一种自包含的声明传输格式,确保通信安全。两者结合使用,能有效进行身份验证及授权管理。然而,密钥管理和 JWT 有效期设置等仍是挑战,需谨慎处理以保障整体安全性。正确配置这些工具和技术,可为用户提供既安全又便捷的服务体验。
45 7
|
3月前
|
JSON 安全 数据安全/隐私保护
Python安全新篇章:OAuth与JWT携手,开启认证与授权的新时代
【8月更文挑战第6天】随着互联网应用的发展,安全认证与授权变得至关重要。本文介绍OAuth与JWT两种关键技术,并展示如何结合它们构建安全系统。OAuth允许用户授权第三方应用访问特定信息,无需分享登录凭证。JWT是一种自包含的信息传输格式,用于安全地传递信息。通过OAuth认证用户并获取JWT,可以验证用户身份并保护数据安全,为用户提供可靠的身份验证体验。
50 6
|
3月前
|
JSON 人工智能 算法
Golang 搭建 WebSocket 应用(四) - jwt 认证
Golang 搭建 WebSocket 应用(四) - jwt 认证
55 0
|
1月前
|
JSON 安全 算法
下一篇
无影云桌面