它不能替代Spring Security,但是在生产中已经有两年多的历史了。
我将尝试尽可能详细地描述整个过程,从生成JWT的密钥到控制器,以便即使是不熟悉JWT的人也可以理解所有内容。

内容
- 背景
- 密钥生成
- 春季项目创建
- 令牌处理器
- 注释和处理程序
- 处理AuthenticationException
- 控制者
0.背景
首先,我想告诉您什么正是促使我实施这种客户端身份验证方法的原因,以及为什么我不使用Spring Security。如果您不感兴趣,则可以跳到下一章。
那时,我在一家开发网站的小公司工作。这是我在这方面的第一份工作,所以我什么都不知道。经过大约一个月的工作,他们说将会有一个新项目,有必要为其准备基本功能。我决定更详细地了解如何在现有项目中实施此过程。令我遗憾的是,那里的一切都不尽如人意。
在控制器的每种方法中,有必要拉出授权用户,都有类似以下内容
@RequestMapping(value = "/endpoint", method = RequestMethod.GET)
public Response endpoint() {
User user = getUser(); //
if (null == user)
return new ErrorResponse.Builder(Error.AUTHENTICATION_ERROR).build();
//
}
因此无处不在...添加新端点始于复制了这段代码。我发现这是一个
为了解决这个问题,我去了谷歌。也许我在寻找问题,但找不到合适的解决方案。有关配置Spring Security的说明无处不在。
让我解释一下为什么我不想使用Spring Security。在我看来,它似乎太复杂了,以某种方式在REST中使用它并不是很方便。在端点处理方法中,您可能仍然必须使用户脱离上下文。也许我错了,因为我对此了解不多,但是无论如何,这篇文章并不是关于这一点的。
我需要一些简单易用的东西。这个想法是通过注释来实现的。
我们的想法是将用户注入需要授权的控制器的每种方法中。就这样。事实证明,在controller方法内已经有一个授权用户,并且它将为!= Null(除非不需要授权的情况除外)。
我们找出了制造这款自行车的原因。现在让我们开始练习。
1.密钥生成
首先,我们需要生成一个密钥,该密钥将加密有关用户的最少必需信息。
有一个非常方便的库,可以使用jwt在Java中工作。
github上有关于如何使用jwt的所有说明,但是为了简化过程,我将在下面给出一个示例。
要生成密钥,请创建一个常规的Maven项目并添加以下依赖项
依存关系
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
而将产生秘密的类
SecretGenerator.java
package jwt;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
public class SecretGenerator {
public static void main(String[] args) {
SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
String secretString = Encoders.BASE64.encode(secretKey.getEncoded());
System.out.println(secretString);
}
}
结果,我们获得了一个秘密密钥,我们将在将来使用它。
2.创建一个Spring项目
我不会描述创建过程,因为有很多关于此主题的文章和教程。在Spring官方网站上有一个Initializer,您可以单击两次以创建一个最小的项目。
我只会留下最后的pom文件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<groupId>org.website</groupId>
<artifactId>backend</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>14</java.version>
<start-class>org.website.BackendWebsiteApplication</start-class>
</properties>
<profiles>
<profile>
<id>local</id>
<properties>
<activatedProperties>local</activatedProperties>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<!--*******SPRING*******-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--*******JWT*******-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<!--*******OTHER*******-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.14</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--*******TEST*******-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
创建项目后,将先前创建的密钥复制到application.properties
app.api.jwtEncodedSecretKey=teTN1EmB5XADI5iV4daGVAQhBlTwLMAE+LlXZp1JPI2PoQOpgVksRqe79EGOc5opg+AmxOOmyk8q1RbfSWcOyg==
3.令牌处理器
我们将需要一个用于生成和解密令牌的服务。
令牌将包含有关用户的最少信息(仅用户ID)和令牌到期时间。为此,我们将创建接口。
转移令牌的生命周期。
Expiration.java
package org.website.jwt;
import java.time.LocalDateTime;
import java.util.Optional;
public interface Expiration {
Optional<LocalDateTime> getAuthTokenExpire();
}
并用于传输ID。它将由用户实体实现
CreateBy.java
package org.website.jwt;
public interface CreateBy {
Long getId();
}
我们还将为Expiration接口创建一个默认实现。默认情况下,令牌将保留24小时。
DefaultExpiration.java
package org.website.jwt;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Optional;
@Component
public class DefaultExpiration implements Expiration {
@Override
public Optional<LocalDateTime> getAuthTokenExpire() {
return Optional.of(LocalDateTime.now().plusHours(24));
}
}
让我们添加几个辅助类。
GeneratedTokenInfo-有关生成的令牌的信息。
TokenInfo-用于获取有关我们的令牌的信息。
GeneratedTokenInfo.java
package org.website.jwt;
import java.time.LocalDateTime;
import java.util.Optional;
public class GeneratedTokenInfo {
private final String token;
private final LocalDateTime expiration;
public GeneratedTokenInfo(String token, LocalDateTime expiration) {
this.token = token;
this.expiration = expiration;
}
public String getToken() {
return token;
}
public LocalDateTime getExpiration() {
return expiration;
}
public Optional<String> getSignature() {
if (null != this.token && this.token.length() >= 3)
return Optional.of(this.token.split("\\.")[2]);
return Optional.empty();
}
}
TokenInfo.java
package org.website.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import lombok.NonNull;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class TokenInfo {
private final Jws<Claims> claimsJws;
private final String signature;
private final Claims body;
private final Long userId;
private final LocalDateTime expiration;
private TokenInfo() {
throw new UnsupportedOperationException();
}
private TokenInfo(@NonNull final Jws<Claims> claimsJws,
@NonNull final String signature,
@NonNull final Claims body,
@NonNull final Long userId,
@NonNull final LocalDateTime expiration) {
this.claimsJws = claimsJws;
this.signature = signature;
this.body = body;
this.userId = userId;
this.expiration = expiration;
}
public static TokenInfo fromClaimsJws(@NonNull final Jws<Claims> claimsJws) {
final Claims body = claimsJws.getBody();
return new TokenInfo(
claimsJws,
claimsJws.getSignature(),
body,
Long.parseLong(body.getId()),
body.getExpiration().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
}
public Jws<Claims> getClaimsJws() {
return claimsJws;
}
public String getSignature() {
return signature;
}
public Claims getBody() {
return body;
}
public Long getUserId() {
return userId;
}
public LocalDateTime getExpiration() {
return expiration;
}
}
现在是TokenHandler本身。它将在用户授权后生成令牌,并检索有关先前授权用户所使用的令牌的信息。
TokenHandler.java
package org.website.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.Optional;
@Service
@Slf4j
public class TokenHandler {
@Value("${app.api.jwtEncodedSecretKey}")
private String jwtEncodedSecretKey;
private final DefaultExpiration defaultExpiration;
private SecretKey secretKey;
@Autowired
public TokenHandler(final DefaultExpiration defaultExpiration) {
this.defaultExpiration = defaultExpiration;
}
@PostConstruct
private void postConstruct() {
byte[] decode = Base64.getDecoder().decode(jwtEncodedSecretKey);
this.secretKey = new SecretKeySpec(decode, 0, decode.length, "HmacSHA512");
}
public Optional<GeneratedTokenInfo> generateToken(CreateBy createBy, Expiration expire) {
if (null == expire || expire.getAuthTokenExpire().isEmpty())
expire = this.defaultExpiration;
try {
final LocalDateTime expireDateTime = expire.getAuthTokenExpire().get().withNano(0);
String compact = Jwts.builder()
.setId(String.valueOf(createBy.getId()))
.setExpiration(Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant()))
.signWith(this.secretKey)
.compact();
return Optional.of(new GeneratedTokenInfo(compact, expireDateTime));
} catch (Exception e) {
log.error("Error generate new token. CreateByID: {}; Message: {}", createBy.getId(), e.getMessage());
}
return Optional.empty();
}
public Optional<GeneratedTokenInfo> generateToken(CreateBy createBy) {
return this.generateToken(createBy, this.defaultExpiration);
}
public Optional<TokenInfo> extractTokenInfo(final String token) {
try {
Jws<Claims> claimsJws = Jwts.parserBuilder()
.setSigningKey(this.secretKey)
.build()
.parseClaimsJws(token);
return Optional.ofNullable(claimsJws).map(TokenInfo::fromClaimsJws);
} catch (Exception e) {
log.error("Error extract token info. Message: {}", e.getMessage());
}
return Optional.empty();
}
}
我不会引起您的注意,因为与此相关的所有事情都应该清楚。
4.注释和处理程序
因此,在完成所有准备工作之后,让我们继续进行最有趣的事情。如前所述,我们需要一个注解,该注解将被注入到需要授权用户的控制器方法中。
使用以下代码创建注释
AuthUser.java
package org.website.annotation;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthUser {
boolean required() default true;
}
早先有人说授权可能是可选的。为此,我们需要摘要中要求的方法。如果对特定方法的授权是可选的,并且如果传入用户确实未被授权,则将null注入该方法。但是我们将为此做好准备。
注释已创建,但是仍然需要一个处理程序,该处理程序将从请求中检索令牌,从用户库接收令牌并将其传递给controller方法。对于这种情况,Spring具有HandlerMethodArgumentResolver接口。我们将执行它。
创建实现上述接口的AuthUserHandlerMethodArgumentResolver类。
AuthUserHandlerMethodArgumentResolver.java
package org.website.annotation.handler;
import org.springframework.core.MethodParameter;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.support.WebArgumentResolver;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.util.WebUtils;
import org.website.annotation.AuthUser;
import org.website.annotation.exception.AuthenticationException;
import org.website.domain.User;
import org.website.domain.UserJwtSignature;
import org.website.jwt.TokenHandler;
import org.website.service.repository.UserJwtSignatureService;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Optional;
public class AuthUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final String AUTH_COOKIE_NAME;
private final String AUTH_HEADER_NAME;
private final TokenHandler tokenHandler;
private final UserJwtSignatureService userJwtSignatureService;
public AuthUserHandlerMethodArgumentResolver(final String authTokenCookieName,
final String authTokenHeaderName,
final TokenHandler tokenHandler,
final UserJwtSignatureService userJwtSignatureService) {
this.AUTH_COOKIE_NAME = authTokenCookieName;
this.AUTH_HEADER_NAME = authTokenHeaderName;
this.tokenHandler = tokenHandler;
this.userJwtSignatureService = userJwtSignatureService;
}
@Override
public boolean supportsParameter(@NonNull final MethodParameter methodParameter) {
return methodParameter.getParameterAnnotation(AuthUser.class) != null && methodParameter.getParameterType().equals(User.class);
}
@Override
public Object resolveArgument(@NonNull final MethodParameter methodParameter,
final ModelAndViewContainer modelAndViewContainer,
@NonNull final NativeWebRequest nativeWebRequest,
final WebDataBinderFactory webDataBinderFactory) throws Exception {
if (!this.supportsParameter(methodParameter))
return WebArgumentResolver.UNRESOLVED;
// required
final boolean required = Objects.requireNonNull(methodParameter.getParameterAnnotation(AuthUser.class)).required();
// HttpServletRequest
Optional<HttpServletRequest> httpServletRequestOptional = Optional.ofNullable(nativeWebRequest.getNativeRequest(HttpServletRequest.class));
//
Optional<UserJwtSignature> userJwtSignature =
this.extractAuthTokenFromRequest(nativeWebRequest, httpServletRequestOptional.orElse(null))
.flatMap(tokenHandler::extractTokenInfo)
.flatMap(userJwtSignatureService::extractByTokenInfo);
if (required) {
//
if (userJwtSignature.isEmpty() || null == userJwtSignature.get().getUser())
//
throw new AuthenticationException(httpServletRequestOptional.map(HttpServletRequest::getMethod).orElse(null),
httpServletRequestOptional.map(HttpServletRequest::getRequestURI).orElse(null));
final User user = userJwtSignature.get().getUser();
//
return this.appendCurrentSignature(user, userJwtSignature.get());
} else {
// , , null
return this.appendCurrentSignature(userJwtSignature.map(UserJwtSignature::getUser).orElse(null),
userJwtSignature.orElse(null));
}
}
private User appendCurrentSignature(User user, UserJwtSignature userJwtSignature) {
Optional.ofNullable(user).ifPresent(u -> u.setCurrentSignature(userJwtSignature));
return user;
}
private Optional<String> extractAuthTokenFromRequest(@NonNull final NativeWebRequest nativeWebRequest,
final HttpServletRequest httpServletRequest) {
return Optional.ofNullable(httpServletRequest)
.flatMap(this::extractAuthTokenFromRequestByCookie)
.or(() -> this.extractAuthTokenFromRequestByHeader(nativeWebRequest));
}
private Optional<String> extractAuthTokenFromRequestByCookie(final HttpServletRequest httpServletRequest) {
return Optional
.ofNullable(httpServletRequest)
.map(request -> WebUtils.getCookie(httpServletRequest, AUTH_COOKIE_NAME))
.map(Cookie::getValue);
}
private Optional<String> extractAuthTokenFromRequestByHeader(@NonNull final NativeWebRequest nativeWebRequest) {
return Optional.ofNullable(nativeWebRequest.getHeader(AUTH_HEADER_NAME));
}
}
在构造函数中,我们接受cookie的名称以及可以在其中传递令牌的标头。我在application.properties中将它们取出
app.api.tokenKeyName=Auth-Token
app.api.tokenHeaderName=Auth-Token
先前创建的TokenHandler和UserJwtSignatureService也将在构造函数中传递。
我们将不考虑UserJwtSignatureService,因为通过ID和令牌签名从数据库中对用户进行了标准提取。
但是,让我们更详细地分析处理程序本身的代码。
supportsParameter-检查方法是否满足要求。
resolveArgument是主要方法,在其中所有“魔术”都会发生。
所以这是怎么回事:
- 我们从注释中获得了必填字段值
- HttpServletRequest
- ,
- required, , .
, , ( , ).
, , , . - , required, , null
注释处理器已创建。但这还不是全部。需要注册才能让Spring知道。这里的一切都很简单。创建一个实现Spring的WebMvcConfigurer接口的配置文件,并覆盖addArgumentResolvers方法
WebMvcConfig.java
package org.website.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.website.annotation.handler.AuthUserHandlerMethodArgumentResolver;
import org.website.jwt.TokenHandler;
import org.website.service.repository.UserJwtSignatureService;
import java.util.List;
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${app.api.tokenKeyName}")
private String tokenKeyName;
@Value("${app.api.tokenHeaderName}")
private String tokenHeaderName;
private final TokenHandler tokenHandler;
private final UserJwtSignatureService userJwtSignatureService;
@Autowired
public WebMvcConfig(final TokenHandler tokenHandler,
final UserJwtSignatureService userJwtSignatureService) {
this.tokenHandler = tokenHandler;
this.userJwtSignatureService = userJwtSignatureService;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new AuthUserHandlerMethodArgumentResolver(
this.tokenKeyName,
this.tokenHeaderName,
this.tokenHandler,
this.userJwtSignatureService));
}
}
这样就完成了注释的编写。
5.处理AuthenticationException
在上一节中,在注释处理程序中,如果controller方法需要授权,但未授权用户,则抛出AuthenticationException。
现在,我们需要添加此异常的类并对其进行处理,以便将json和所需的信息返回给用户。
AuthenticationException.java
package org.website.annotation.exception;
public class AuthenticationException extends Exception {
public AuthenticationException(String requestMethod, String url) {
super(String.format("%s - %s", requestMethod, url));
}
}
现在是异常处理程序本身。为了处理已发生的异常并为用户提供一些标准的Spring错误页面(而非我们需要的json),Spring提供了ControllerAdvice批注。
让我们添加一个用于处理执行的类。
AuthenticationExceptionControllerAdvice.java
package org.website.controller.exception.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.website.annotation.exception.AuthenticationException;
import org.website.http.response.Error;
import org.website.http.response.ErrorResponse;
import org.website.http.response.Response;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@ControllerAdvice
@Slf4j
public class AuthenticationExceptionControllerAdvice extends AbstractControllerAdvice {
@Value("${app.api.tokenKeyName}")
private String tokenKeyName;
@ExceptionHandler({AuthenticationException.class})
public Response authenticationException(HttpServletResponse response) {
Cookie cookie = new Cookie(tokenKeyName, "");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
return new ErrorResponse.Builder(Error.AUTHENTICATION_ERROR).build();
}
}
现在,如果抛出AuthenticationException,它将被捕获,并且将向用户返回带有AUTHENTICATION_ERROR错误的json
6.控制器
现在,实际上是为了一切而开始。让我们用3种方法创建一个控制器:
- 强制授权
- 随着无强制性认证
- 注册新用户。最少的代码。它只是将用户保存到数据库,没有密码。这也将返回新用户的令牌
TestAuthController.java
package org.website.controller;
import com.google.gson.JsonObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.website.annotation.AuthUser;
import org.website.domain.User;
import org.website.http.response.Response;
import org.website.http.response.SuccessResponse;
import org.website.jwt.GeneratedTokenInfo;
import org.website.service.repository.UserJwtSignatureService;
import org.website.service.repository.UserService;
import java.util.Optional;
@RestController
@RequestMapping("/test-auth")
public class TestAuthController {
@Autowired
private UserService userService;
@Autowired
private UserJwtSignatureService userJwtSignatureService;
@RequestMapping(value = "/required", method = RequestMethod.GET)
public Response required(@AuthUser final User user) {
return new SuccessResponse.Builder(user).build();
}
@RequestMapping(value = "/not-required", method = RequestMethod.GET)
public Response notRequired(@AuthUser(required = false) final User user) {
JsonObject response = new JsonObject();
if (null == user) {
response.addProperty("message", "Hello guest!");
} else {
response.addProperty("message", "Hello " + user.getFirstName());
}
return new SuccessResponse.Builder(response).build();
}
@RequestMapping(value = "/sign-up", method = RequestMethod.GET)
public Response signUp(@RequestParam String firstName) {
User user = userService.save(User.builder().firstName(firstName).build());
Optional<GeneratedTokenInfo> generatedTokenInfoOptional =
userJwtSignatureService.generateNewTokenAndSaveToDb(user);
return new SuccessResponse.Builder(user)
.addPropertyToPayload("token", generatedTokenInfoOptional.get().getToken())
.build();
}
}
在required和notRequired方法中,我们插入注释。
在第一种情况下,如果未授权用户,则应返回错误的json,如果已授权,则将返回有关用户的信息。
在第二种情况下,如果用户未登录,则消息Hello guest!,如果获得授权,将返回其名称。
让我们检查一下是否一切正常。
首先,让我们以未授权用户的身份检查这两种方法。
一切都如预期。需要授权的地方,将返回错误,在第二种情况下,将显示消息Hello guest!...
现在让我们注册并尝试调用相同的方法,只是在请求标头中传递令牌。
响应返回一个令牌,该令牌可用于需要授权的那些请求。
让我们检查一下:
在第一种情况下,仅返回有关用户的信息。在第二种情况下,将返回欢迎消息。
加工!
7.结论
此方法并不声称是唯一正确的解决方案。有人可能更喜欢使用Spring Security。但是,正如一开始提到的那样,该方法已被证明,易于使用且效果很好。