详解用JWT对SpringCloud进行认证和鉴权

详解用JWT对SpringCloud进行认证和鉴权

什么是JWT

JWT (JSON Web Token) 是一种开放标准 (RFC 7519),它定义了一种简洁的、自包含的方式,用于在不同的系统之间传递安全信息。JWT 通常由 3 部分组成:头部 (header)、载荷 (payload)、签名 (signature)。其中,头部用于描述 JWT 的元数据,载荷是 JWT 中存放有效信息的地方,签名则是用于验证 JWT 真实性的。

JWT在Spring Cloud中的鉴权和认证

在微服务架构中,通常会有多个服务协同工作,因此需要对服务进行鉴权和认证,确保只有经过授权的用户才能访问相应的服务,这时就需要使用 JWT。

Spring Cloud 提供了多种 JWT 鉴权和认证的解决方案,其中最常用的是 Spring Security + JWT。具体实现步骤如下:

  1. 引入 Spring Security、JWT 相关依赖:

```xml

org.springframework.cloud
spring-cloud-starter-oauth2


io.jsonwebtoken
jjwt-api
0.11.2


io.jsonwebtoken
jjwt-impl
0.11.2
runtime


io.jsonwebtoken
jjwt-jackson
0.11.2
runtime

```

  1. 配置 Spring Security:

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Autowired
   private JwtTokenProvider jwtTokenProvider;

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
               .authorizeRequests().antMatchers("/login", "/register").permitAll().anyRequest().authenticated().and()
               .apply(new JwtConfigurer(jwtTokenProvider));
   }

}
```

  1. 实现 JwtTokenProvider 接口:

```java
public interface JwtTokenProvider {
String createToken(String subject, Map claims);

   Claims getClaims(String token);

   boolean validateToken(String token);

   String getUsername(String token);

   List<String> getRoles(String token);

   String resolveToken(HttpServletRequest request);

}
```

  1. 实现 JwtTokenProvider 接口的具体方法:

```java
@Component
public class JwtTokenProviderImpl implements JwtTokenProvider {

   private static final Logger logger = LoggerFactory.getLogger(JwtTokenProviderImpl.class);

   @Value("${jwt.secret}")
   private String secretKey;

   @Value("${jwt.token.validity}")
   private long validityInMilliseconds;

   @Override
   public String createToken(String subject, Map<String, Object> claims) {
       Date now = new Date();
       Date validity = new Date(now.getTime() + validityInMilliseconds);

       return Jwts.builder().setSubject(subject).setClaims(claims).setIssuedAt(now).setExpiration(validity)
               .signWith(SignatureAlgorithm.HS256, secretKey).compact();
   }

   @Override
   public Claims getClaims(String token) {
       return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
   }

   @Override
   public boolean validateToken(String token) {
       try {
           Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
           return true;
       } catch (JwtException | IllegalArgumentException e) {
           logger.error("Expired or invalid JWT token: {}", e.getMessage());
       }
       return false;
   }

   @Override
   public String getUsername(String token) {
       return getClaims(token).getSubject();
   }

   @Override
   @SuppressWarnings("unchecked")
   public List<String> getRoles(String token) {
       return (List<String>) getClaims(token).get("roles");
   }

   @Override
   public String resolveToken(HttpServletRequest request) {
       String bearerToken = request.getHeader("Authorization");
       if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
           return bearerToken.substring(7);
       }
       return null;
   }

}
```

  1. 实现 JwtConfigurer:

```java
public class JwtConfigurer extends SecurityConfigurerAdapter {

   private JwtTokenProvider jwtTokenProvider;

   public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
       this.jwtTokenProvider = jwtTokenProvider;
   }

   @Override
   public void configure(HttpSecurity http) throws Exception {
       JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
       http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
   }

}
```

  1. 实现 JwtTokenFilter:

```java
public class JwtTokenFilter extends OncePerRequestFilter {

   private JwtTokenProvider jwtTokenProvider;

   public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
       this.jwtTokenProvider = jwtTokenProvider;
   }

   @Override
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
           throws ServletException, IOException {
       String token = jwtTokenProvider.resolveToken(request);
       if (token != null && jwtTokenProvider.validateToken(token)) {
           Authentication auth = jwtTokenProvider.getAuthentication(token);
           SecurityContextHolder.getContext().setAuthentication(auth);
       }
       filterChain.doFilter(request, response);
   }

}
```

示例1:使用Postman测试Spring Cloud中的JWT鉴权和认证

  1. 启动 Spring Cloud 中的服务,确保服务在本地运行,例如,服务的端口为 8080。

  2. 使用 Postman 发送 GET 请求时,先不进行 JWT认证。可以通过以下两种方式测试:

  3. 访问没有配置 JWT 鉴权和认证的接口,例如查询某个用户的基本信息。由于该接口没有访问权限限制,可以正常访问。

  4. 访问已经配置 JWT 鉴权和认证的接口,例如查询某个订单的详细信息。由于没有 JWT 认证(即没有传入有效的 JWT token),应返回 401 错误码。

  5. 使用 Postman 发送 POST 请求时,需要进行 JWT 认证。具体步骤如下:

  6. 以管理员身份登录,获取有效的 JWT token。

  7. 在 Postman 的请求头中设置 Authorization 字段,其值为 Bearer 后接上述获取的 JWT token。

  8. 发送包含有效 JWT token 的 POST 请求,例如新增商品信息。由于设置了 JWT 鉴权和认证,只有管理员身份的用户可以访问该接口,其他用户将被拒绝访问。

  9. 发送包含无效 JWT token 的 POST 请求,例如删除某个商品。由于 JWT 认证不通过,应返回 401 错误码。

示例2:使用Istio的JWT鉴权

  1. 部署 Istio 服务网格,可以参考官方文档:https://istio.io/latest/docs/setup/install/

  2. 部署 Spring Cloud 中的服务,并将服务注册到 Istio 中:

  3. 在 Spring Cloud 的服务配置文件中增加以下配置:

    yaml
    eureka:
    instance:
    metadataMap:
    istio:
    workloadLabels: "app=<your-spring-cloud-app>"

    其中,<your-spring-cloud-app> 为 Spring Cloud 服务的名称。

  4. 在 Istio 的 DestinationRule 配置中增加以下配置:

    yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
    name: <your-spring-cloud-app>-destination
    spec:
    host: <your-spring-cloud-app>
    trafficPolicy:
    tls:
    mode: DISABLE
    connectionPool:
    http:
    http1MaxPendingRequests: 1024
    maxRequestsPerConnection: 1024
    tcp:
    maxConnections: 1024

    其中,<your-spring-cloud-app> 为 Spring Cloud 服务的名称。

  5. 配置 Istio 中的 JWT 鉴权:

  6. 创建 JWT 证书:

    sh
    # 生成私钥
    openssl genrsa -out ./jwt.key 4096
    # 生成公钥
    openssl rsa -in ./jwt.key -pubout -out ./jwt.pub

    其中,jwt.key 为生成的私钥文件,jwt.pub 为生成的公钥文件。

  7. 创建 Istio 的 AuthorizationPolicy 配置:

    yaml
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
    name: <your-spring-cloud-app>-authorization
    spec:
    selector:
    matchLabels:
    app: <your-spring-cloud-app>
    version: v1
    action: ALLOW
    rules:
    - from:
    - source:
    requestPrincipals: ["*"]
    to:
    - operation:
    methods: ["*"]

    其中,<your-spring-cloud-app> 为 Spring Cloud 服务的名称,v1 为服务的版本。

  8. 测试 JWT 鉴权:

  9. 使用 jwt-cli 工具,生成有效的 JWT token:

    sh
    # 生成包含 username 和 roles 的 JWT token
    jwt encode --secret <your-secret-key> --alg HS256 --sub <your-username> --claims roles=<your-roles>

    其中,<your-secret-key> 为自定义的密钥,<your-username> 为用户名,<your-roles> 为用户角色信息。

  10. 在 Postman 的请求头中设置 Authorization 字段,其值为 Bearer 后接上述生成的有效 JWT token。

  11. 发送 GET 请求,例如查询某个订单的详细信息。由于 JWT 鉴权和认证通过,可以正常访问该接口。

  12. 发送 GET 请求,例如查询某个用户的基本信息。由于未设置 JWT 鉴权和认证,可以正常访问该接口。

以上就是使用 JWT 对 Spring Cloud 进行认证和鉴权的详细攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解用JWT对SpringCloud进行认证和鉴权 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • MyBatis批量插入的五种方式小结(MyBatis以集合方式批量新增)

    MyBatis批量插入的五种方式小结 在使用MyBatis进行批量插入时,有多种方式可以选择。本文将介绍MyBatis批量插入的五种方式,并提供示例代码,以便读者更好地理解这些方法。 方式一:使用for循环单条插入 在使用for循环单条插入时,需要在for循环中执行insert语句。这种方式的优点是插入的数据可以轻松地进行转换,缺点是插入效率较低。 priv…

    Java 2023年6月1日
    00
  • 史上最全MyBatis面试题及答案

    史上最全MyBatis面试题及答案攻略 什么是MyBatis?它的作用是什么? MyBatis是一个持久层框架,用于简化Java应用程序中的数据库交互。它使用XML或注解来描述对象映射器,从而实现将Java对象映射为数据库表中的数据。MyBatis的主要作用是:简化数据库交互代码的编写,防止SQL注入攻击,提高代码的可维护性和可读性。 MyBatis中的Ma…

    Java 2023年5月20日
    00
  • Java实战之课程信息管理系统的实现

    Java实战之课程信息管理系统的实现 项目简介 课程信息管理系统是一个简单的管理应用程序,它可以帮助学生和教师管理课程信息,包括课程的添加、查询、修改、删除等操作。该系统采用Java语言进行开发,具有良好的可拓展性和易维护性,可以运行在各种平台上。 开发环境 Java SE Development Kit 8 (JDK 8) Eclipse IDE MySQ…

    Java 2023年5月23日
    00
  • Java的反射机制—动态调用对象的简单方法

    Java的反射机制—动态调用对象的简单方法 Java反射机制是指程序在运行时可以获取自身的信息,并能够操作类或者对象的属性、方法和构造方法。反射机制可以在运行时动态地获取对象的信息,而不需要事先知道构造函数、方法、属性等信息。在Java中反射机制有很多应用场景,最常见的就是在框架中通过获取类信息动态创建对象实例、调用类的方法等。 具体步骤 使用Java反…

    Java 2023年5月26日
    00
  • 实例讲解Java批量插入、更新数据

    来详细讲解一下“实例讲解Java批量插入、更新数据”的完整攻略吧。 思路概述 在 Java 中批量插入、更新数据的基本思路是: 手动拼接 SQL 语句,将多条插入语句合并成一条; 执行批量插入、更新操作; 对于第一步手动拼接 SQL 语句,为了避免 SQL 注入,一般会使用 PreparedStatement 或 NamedParameterJdbcTemp…

    Java 2023年5月20日
    00
  • Java Predicate及Consumer接口函数代码实现解析

    Java中的Predicate和Consumer是两种常用的函数式接口,它们可以让我们编写更为简洁、灵活的代码,特别是在处理集合、流等数据时非常有用。 Predicate Predicate可以理解为谓词或者断言,它接受一个输入参数,返回一个布尔类型的值。通常情况下,我们使用Predicate来过滤集合或者流中的数据。 下面是Predicate接口的定义: …

    Java 2023年5月26日
    00
  • 页面的缓存与不缓存设置及html页面中meta的作用

    页面缓存是浏览器缓存方式之一,也是提高网站性能的重要手段之一。Web页面中通过使用HTTP头,让浏览器在本地缓存页面,以避免重复网络请求。本文将对页面缓存和不缓存设置进行详细讲解,并介绍HTML页面中meta标签的作用。 页面缓存的作用 页面缓存是将网站的静态资源如CSS、JS、图片等文件保存在本地,下次打开同样的页面,在一段时间内可以直接从缓存中读取,从而…

    Java 2023年6月16日
    00
  • Java利用自定义注解、反射实现简单BaseDao实例

    下面是详细的Java利用自定义注解、反射实现简单BaseDao实例的攻略: 一、什么是自定义注解? 自定义注解是一种特殊的interface,它和普通接口有些类似,但是它仅仅是一种标记,没有任何具体的方法。Java提供了元注解来为注解提供信息,元注解可以放在注解前面,用于描述注解本身的信息。 二、自定义注解的作用? 自定义注解常用于标记,其作用就是给编译器、…

    Java 2023年6月1日
    00
合作推广
合作推广
分享本页
返回顶部