Обновил версии библиотек. Заменил устаревшие подходы на современные.

This commit is contained in:
Struchkov Mark 2022-06-21 11:47:14 +03:00
parent 6a3ea3f134
commit 3db2b2dafd
22 changed files with 493 additions and 121 deletions

43
pom.xml
View File

@ -5,43 +5,64 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.sadtech.example.jwt</groupId>
<groupId>dev.struchkiov.example</groupId>
<artifactId>server-jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>server-jwt</name>
<description>server-jwt</description>
<properties>
<java.version>11</java.version>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.4.0-b180830.0359</version>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server;
package dev.struchkov.example.jwt.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -0,0 +1,38 @@
package dev.struchkov.example.jwt.server.config;
import dev.struchkov.example.jwt.server.filter.JwtFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private final JwtFilter jwtFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests(
authz -> authz
.antMatchers("/api/auth/login", "/api/auth/token").permitAll()
.anyRequest().authenticated()
.and()
.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class)
).build();
}
}

View File

@ -1,10 +1,10 @@
package org.sadtech.example.jwt.server.controller;
package dev.struchkov.example.jwt.server.controller;
import dev.struchkov.example.jwt.server.domain.JwtResponse;
import dev.struchkov.example.jwt.server.domain.RefreshJwtRequest;
import dev.struchkov.example.jwt.server.service.AuthService;
import lombok.RequiredArgsConstructor;
import org.sadtech.example.jwt.server.domain.JwtRequest;
import org.sadtech.example.jwt.server.domain.JwtResponse;
import org.sadtech.example.jwt.server.domain.RefreshJwtRequest;
import org.sadtech.example.jwt.server.service.AuthService;
import dev.struchkov.example.jwt.server.domain.JwtRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

View File

@ -1,8 +1,8 @@
package org.sadtech.example.jwt.server.controller;
package dev.struchkov.example.jwt.server.controller;
import dev.struchkov.example.jwt.server.service.AuthService;
import lombok.RequiredArgsConstructor;
import org.sadtech.example.jwt.server.domain.JwtAuthentication;
import org.sadtech.example.jwt.server.service.AuthService;
import dev.struchkov.example.jwt.server.domain.JwtAuthentication;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
@ -20,7 +20,6 @@ public class Controller {
@GetMapping("hello/user")
public ResponseEntity<String> helloUser() {
final JwtAuthentication authInfo = authService.getAuthInfo();
return ResponseEntity.ok("Hello user " + authInfo.getPrincipal() + "!");
}

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server.domain;
package dev.struchkov.example.jwt.server.domain;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server.domain;
package dev.struchkov.example.jwt.server.domain;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server.domain;
package dev.struchkov.example.jwt.server.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server.domain;
package dev.struchkov.example.jwt.server.domain;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server.domain;
package dev.struchkov.example.jwt.server.domain;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;

View File

@ -1,4 +1,4 @@
package org.sadtech.example.jwt.server.domain;
package dev.struchkov.example.jwt.server.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -9,8 +9,8 @@ import java.util.Set;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String login;

View File

@ -0,0 +1,14 @@
package dev.struchkov.example.jwt.server.exception;
/**
* Исключение используется для ошибок аутентификации и авторизациит.
*
* @author upagge 21.06.2022
*/
public class AuthException extends RuntimeException {
public AuthException(String message) {
super(message);
}
}

View File

@ -1,11 +1,11 @@
package org.sadtech.example.jwt.server.filter;
package dev.struchkov.example.jwt.server.filter;
import dev.struchkov.example.jwt.server.service.JwtProvider;
import dev.struchkov.example.jwt.server.service.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.example.jwt.server.domain.JwtAuthentication;
import org.sadtech.example.jwt.server.service.JwtProvider;
import org.sadtech.example.jwt.server.service.JwtUtils;
import dev.struchkov.example.jwt.server.domain.JwtAuthentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

View File

@ -1,13 +1,13 @@
package org.sadtech.example.jwt.server.service;
package dev.struchkov.example.jwt.server.service;
import io.jsonwebtoken.Claims;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.example.jwt.server.domain.JwtAuthentication;
import org.sadtech.example.jwt.server.domain.JwtRequest;
import org.sadtech.example.jwt.server.domain.JwtResponse;
import org.sadtech.example.jwt.server.domain.User;
import org.sadtech.example.jwt.server.exception.AuthException;
import dev.struchkov.example.jwt.server.domain.JwtAuthentication;
import dev.struchkov.example.jwt.server.domain.JwtRequest;
import dev.struchkov.example.jwt.server.domain.JwtResponse;
import dev.struchkov.example.jwt.server.domain.User;
import dev.struchkov.example.jwt.server.exception.AuthException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

View File

@ -1,18 +1,21 @@
package org.sadtech.example.jwt.server.service;
package dev.struchkov.example.jwt.server.service;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.example.jwt.server.domain.User;
import dev.struchkov.example.jwt.server.domain.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.security.Key;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@ -22,54 +25,55 @@ import java.util.Date;
@Component
public class JwtProvider {
private final String jwtAccessSecret;
private final String jwtRefreshSecret;
private final SecretKey jwtAccessSecret;
private final SecretKey jwtRefreshSecret;
public JwtProvider(
@Value("${jwt.secret.access}") String jwtAccessSecret,
@Value("${jwt.secret.refresh}") String jwtRefreshSecret
) {
this.jwtAccessSecret = jwtAccessSecret;
this.jwtRefreshSecret = jwtRefreshSecret;
this.jwtAccessSecret = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtAccessSecret));
this.jwtRefreshSecret = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtRefreshSecret));
}
public String generateAccessToken(@NonNull User user) {
final LocalDateTime now = LocalDateTime.now();
final Instant accessExpirationInstant = now.plusMinutes(5).atZone(ZoneId.systemDefault()).toInstant();
final Date accessExpiration = Date.from(accessExpirationInstant);
final String accessToken = Jwts.builder()
return Jwts.builder()
.setSubject(user.getLogin())
.setExpiration(accessExpiration)
.signWith(SignatureAlgorithm.HS512, jwtAccessSecret)
.signWith(jwtAccessSecret)
.claim("roles", user.getRoles())
.claim("firstName", user.getFirstName())
.compact();
return accessToken;
}
public String generateRefreshToken(@NonNull User user) {
final LocalDateTime now = LocalDateTime.now();
final Instant refreshExpirationInstant = now.plusDays(30).atZone(ZoneId.systemDefault()).toInstant();
final Date refreshExpiration = Date.from(refreshExpirationInstant);
final String refreshToken = Jwts.builder()
return Jwts.builder()
.setSubject(user.getLogin())
.setExpiration(refreshExpiration)
.signWith(SignatureAlgorithm.HS512, jwtRefreshSecret)
.signWith(jwtRefreshSecret)
.compact();
return refreshToken;
}
public boolean validateAccessToken(@NonNull String token) {
return validateToken(token, jwtAccessSecret);
public boolean validateAccessToken(@NonNull String accessToken) {
return validateToken(accessToken, jwtAccessSecret);
}
public boolean validateRefreshToken(@NonNull String token) {
return validateToken(token, jwtRefreshSecret);
public boolean validateRefreshToken(@NonNull String refreshToken) {
return validateToken(refreshToken, jwtRefreshSecret);
}
private boolean validateToken(@NonNull String token, @NonNull String secret) {
private boolean validateToken(@NonNull String token, @NonNull Key secret) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
Jwts.parserBuilder()
.setSigningKey(secret)
.build()
.parseClaimsJws(token);
return true;
} catch (ExpiredJwtException expEx) {
log.error("Token expired", expEx);
@ -93,8 +97,12 @@ public class JwtProvider {
return getClaims(token, jwtRefreshSecret);
}
private Claims getClaims(@NonNull String token, @NonNull String secret) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
private Claims getClaims(@NonNull String token, @NonNull Key secret) {
return Jwts.parserBuilder()
.setSigningKey(secret)
.build()
.parseClaimsJws(token)
.getBody();
}
}

View File

@ -1,10 +1,10 @@
package org.sadtech.example.jwt.server.service;
package dev.struchkov.example.jwt.server.service;
import io.jsonwebtoken.Claims;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.sadtech.example.jwt.server.domain.JwtAuthentication;
import org.sadtech.example.jwt.server.domain.Role;
import dev.struchkov.example.jwt.server.domain.JwtAuthentication;
import dev.struchkov.example.jwt.server.domain.Role;
import java.util.List;
import java.util.Set;

View File

@ -1,9 +1,9 @@
package org.sadtech.example.jwt.server.service;
package dev.struchkov.example.jwt.server.service;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.example.jwt.server.domain.Role;
import org.sadtech.example.jwt.server.domain.User;
import dev.struchkov.example.jwt.server.domain.Role;
import dev.struchkov.example.jwt.server.domain.User;
import org.springframework.stereotype.Service;
import java.util.Collections;

View File

@ -0,0 +1,18 @@
package dev.struchkov.example.jwt.server.util;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
public class GenerateKeys {
public static void main(String[] args) {
System.out.println(generateKey());
System.out.println(generateKey());
}
private static String generateKey() {
return Encoders.BASE64.encode(Keys.secretKeyFor(SignatureAlgorithm.HS512).getEncoded());
}
}

View File

@ -1,44 +0,0 @@
package org.sadtech.example.jwt.server.config;
import lombok.RequiredArgsConstructor;
import org.sadtech.example.jwt.server.filter.JwtFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtFilter jwtFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/login", "/api/auth/token").permitAll()
.anyRequest().authenticated()
.and()
.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -1,9 +0,0 @@
package org.sadtech.example.jwt.server.exception;
public class AuthException extends RuntimeException {
public AuthException(String message) {
super(message);
}
}

View File

@ -1,2 +1,2 @@
jwt.secret.access=supermegasecret
jwt.secret.refresh=supermegarefreshsecret
jwt.secret.access=qBTmv4oXFFR2GwjexDJ4t6fsIUIUhhXqlktXjXdkcyygs8nPVEwMfo29VDRRepYDVV5IkIxBMzr7OEHXEHd37w==
jwt.secret.refresh=zL1HB3Pch05Avfynovxrf/kpF9O2m4NCWKJUjEp27s9J2jEG3ifiKCGylaZ8fDeoONSTJP/wAzKawB8F9rOMNg==

View File

@ -0,0 +1,327 @@
{
"info": {
"_postman_id": "7be0f05f-b637-4296-a2ab-9ae2e622fa16",
"name": "JWT",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "16442716"
},
"item": [
{
"name": "Auth Service",
"item": [
{
"name": "Login User",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"login\": \"anton\",\n \"password\": \"1234\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/auth/login",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"auth",
"login"
]
}
},
"response": []
},
{
"name": "Login Admin",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"login\": \"anton\",\n \"password\": \"1234\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/auth/login",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"auth",
"login"
]
}
},
"response": []
},
{
"name": "Get new access token",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"refreshToken\": \"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbnRvbiIsImV4cCI6MTY1ODM4NTcwMH0.35Xfw495acquYKcHKK2MrRU_dPlNqPQC7N3-vxA2d0zayWD1Ify6J-xYl5tWkm-8qdyXqPCri3uEfpzx1Lc7WA\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/auth/token",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"auth",
"token"
]
}
},
"response": []
},
{
"name": "Get new access and refresh tokens",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbnRvbiIsImV4cCI6MTY1NTc5NDE2NCwicm9sZXMiOlsiVVNFUiJdLCJmaXJzdE5hbWUiOiLQkNC90YLQvtC9In0.hJ_j6BjysvP2Qv2Lt06m8FwE-U4AHRjVQ9BpBy6fJIycUDZxBSAhoeFucaOGFgukTMfICZbgEvna9OuwqYzzwQ",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"refreshToken\": \"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbnRvbiIsImV4cCI6MTY1ODM4NTg2NH0.gfE8Gr_1Sp_Um3vXG2EaDgqz6p9iLo1_wZgKksme13pg2q4cXVyShBtMTZ0ApfdcGzXcJ2MUoFHtTJCMj8ROUQ\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/auth/refresh",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"auth",
"refresh"
]
}
},
"response": []
}
]
},
{
"name": "Service One",
"item": [
{
"name": "Hello User Request",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbnRvbiIsImV4cCI6MTY1NTc5NTEyMCwicm9sZXMiOlsiVVNFUiJdLCJmaXJzdE5hbWUiOiLQkNC90YLQvtC9In0.tLFtJ0oPLq493u7EJe2Tb4kLxAFGCYgIWrmMq1XiHNhihbh2sV9-yVQyXOFwIpArw4ReAuUoP-6F2B_6YYzx4Q",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/hello/user",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"hello",
"user"
]
}
},
"response": []
},
{
"name": "Hello Admin Request",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbnRvbiIsImV4cCI6MTY1NTc5MzYxNiwicm9sZXMiOlsiVVNFUiJdLCJmaXJzdE5hbWUiOiLQkNC90YLQvtC9In0.2JBqOWBOmO9a93nwiBgFvf6LvATMw-DALRlSwjFbshhu5RP110NIg5Aod_V0r1WtNDAbuzsHFxk7N-chy4sHQg",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/hello/admin",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"hello",
"admin"
]
}
},
"response": []
}
]
},
{
"name": "Service Two",
"item": [
{
"name": "Hello User Request",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8099/api/hello/user",
"protocol": "http",
"host": [
"localhost"
],
"port": "8099",
"path": [
"api",
"hello",
"user"
]
}
},
"response": []
},
{
"name": "Hello Admin Request",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhbnRvbiIsImV4cCI6MTY1NTc1NjQyNSwicm9sZXMiOlsiVVNFUiJdLCJmaXJzdE5hbWUiOiLQkNC90YLQvtC9In0.y40_c0QGAMzf3tq19UtfNHdYcU7KS_xCqzzxBDLeUMZ5ait7LfWbLv8hCHyKGNBHOYLuquu5ylTiNBT4DBgr3A",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8099/api/hello/user",
"protocol": "http",
"host": [
"localhost"
],
"port": "8099",
"path": [
"api",
"hello",
"user"
]
}
},
"response": []
}
]
}
]
}