[Project actual combat] Spring Cloud Gateway implements JWT / Token login authentication process

table of Contents

I. Introduction

1. Introduction to Spring Cloud Gateway

2. Introduction to token

Two, login authentication process

3. Project actual combat

1. auth authentication service

2. gateway gateway service


I. Introduction

This article will demonstrate how to create a token when logging in, then verify the token at the gateway, and extract the user information into the Header request header and send it to the downstream business system.

The project uses Spring Cloud Gateway as the gateway for the unified routing of microservices. The auth authentication module is responsible for generating the token, and the gateway gateway module is responsible for verifying the token and unifying the entrance .

The role of the gateway is as an API architecture to protect, enhance, and control access to API services. The API gateway is a system in front of an application or service (providing REST API interface services), used to manage authorization, access control, traffic restriction, etc., so that the REST API interface service is protected by the API gateway and is transparent to all callers . Therefore, the business system hidden behind the API gateway can focus on creating and managing services instead of dealing with these strategic infrastructures.

1. Introduction to Spring Cloud Gateway

Spring Cloud Gateway is an official Spring gateway developed based on technologies such as Spring 5.0, Spring Boot 2.0 and Project Reactor . Spring Cloud Gateway aims to provide a simple and effective unified API routing management method for microservice architecture. As a gateway in the Spring Cloud ecosystem, Spring Cloud Gateway aims to replace ZUUL . It not only provides a unified routing method , but also provides basic functions of the gateway based on the Filter chain, such as: security, monitoring/buried points, and current limiting Wait.

2. Introduction to token

A jwt is actually a string, which consists of three parts, the header , payload, and signature . These three parts are all in json format.

For details, please refer to: https://blog.csdn.net/a1036645146/article/details/103726635

2.1 Header

The header is used to describe the most basic information about the JWT, such as its type and signature algorithm.

{
    "typ": "JWT",
    "alg": "HS256"
}

Here, we explained that this is a JWT, and the signature algorithm we use is the HS256 algorithm.

2.2 Payload

The payload can be used to put some insensitive information.

{
    "iss": "John Wu JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "[email protected]",
    "from_user": "B",
    "target_user": "A"
}

The first five fields are defined by the JWT standard.

  • iss: Issuer of the JWT
  • sub: The user targeted by this JWT
  • aud: The party receiving the JWT
  • exp(expires): When does it expire? Here is a Unix timestamp
  • iat(issued at): when was it issued

Two strings are obtained after Base64 encoding of the header and payload respectively, and then the two encoded strings are .connected with English periods (head first) to form a new string:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0

2.3 Signature

Finally, we use HS256 algorithm to encrypt the above spliced ​​string. When encrypting, we also need to provide a secret. The encrypted content is also a string, and the last string is the signature. By splicing this signature behind the string just now, you can get the complete jwt. If the header part and the payload part are tampered with, since the tamperer does not know what the key is and cannot generate a new signature part , the server will not be able to pass. In jwt, the message body is transparent, and the signature can ensure that the message is not Has been tampered with.

Of course, if a person's token is stolen by others, then I can't help it. I will also think that the thief is a legitimate user, which is actually the same as a person's session id being stolen by others.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJyb2xlcyI6InB1cmNoYXNlciIsInNvdXJjZSI6IlBDIiwidXNlck5hbWUiOiIxNTYyMjEzMzIzMSIsImV4cCI6MTYwNDgwMDM5NSwidXNlcklkIjoiMTE5MCJ9
.zpM6dyakS1N6kySMIEHuOZfN8l4WlybRbq6VK1cDqGc

 

Two, login authentication process

After the user logs in successfully, the authentication service (auth module) generates a token, and all subsequent front-end requests carry this token in the request header. The gateway is responsible for verifying the token and putting the user information into the header of the request so that the downstream system can easily obtain the user information.

The basic authentication process based on Token authentication is as follows:

  1. The user enters the login information (or calls the Token interface to pass in user information) and sends it to the authentication service for authentication. Note: The identity authentication service can be together with the server or separated, depending on the split of the microservices, here is a single auth module.
  2. The authentication service (auth) verifies whether the login information is correct, and returns the Token if it is correct (the general interface will contain the user's basic information, scope of authority, valid time and other information).
  3. The client stores the Token. When requesting another interface again, the Token can be placed in the HTTP request header to initiate related API calls.
  4. To access the back-end interface, the client needs to pass through the gateway. The gateway configures the interceptor uniformly, verifies the Token authority, and then forwards it to the corresponding back-end other microservice interfaces.
  5. After the verification is passed, the server returns related resources and data.

The project involves modules:

  • gateway - gateway service
  • auth - authentication service
  • user-service
  • order-service
  • (Other microservices)...

Each module application is built based on SpringBoot, using Nacos as the configuration center and registration center, Feign is used for interface calls between services, Http interface (REST API) is published, gateway is used as the API unified entrance, front-end requests cannot directly call downstream microservices Specific interface.

3. Project actual combat

1. auth authentication service

Build an auth authentication service module through SpringBoot, and use Nacos in the registration and configuration center.

This module mainly provides login interface and generates token.

1.1 pom.xml

You need to import the jwt toolkit and use the latest stable version.

<dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>${jwt.version}</version>
</dependency>

1.2 JWTUtils.java tool class

/**
 * jwt工具类
 *
 * @author stwen_gan
 * @since 
 */
public class JWTUtils {

    // token 签名的秘钥,可设置到配置文件中
    private static final String SECRET_KEY = "secretKey:123456";
    // token过期时间
    public static final long TOKEN_EXPIRE_TIME = 7200 * 1000;

    /**
     * 生成jwt
     */
    public String createJwt(String userId){
        Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
        //设置头信息
        HashMap<String, Object> header = new HashMap<>(2);
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        // 生成 token:头部+载荷+签名
        return JWT.create().withHeader(header)
                .withClaim(RequestKeyConstants.USER_ID,userId)
                .withExpiresAt(new Date(System.currentTimeMillis()+TOKEN_EXPIRE_TIME)).sign(algorithm);
    }

    /**
     * 解析jwt
     */
    public Map<String, Claim> parseJwt(String token) {
        Map<String, Claim> claims = null;
        try {
            Algorithm algorithm = Algorithm.HMAC256(key);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            claims = jwt.getClaims();
            return claims;
        } catch (Exception e) {
            return null;
        }
    }
}

Note: It is recommended to configure the signature key and expiration time of this tool to the file of the configuration center uniformly, and then pass in the parameters through the method.

Token composition:

  • head
{
    "typ": "JWT",
    "alg": "HS256"
}
  • Payload: The user stores some non-sensitive user information (such as user id, user name, permissions, roles, etc.)
  • Signature: Sign the above two parts with a given secret key

1.3 Login interface

LoginController

Note: Part of the code is omitted for the convenience of demonstration.

@PostMapping("/auth/login")
public Result login(HttpServletRequest request, @Valid @RequestBody LoginDTO loginDto) {

    String ip = IpAddressUtils.getIpAddr(request);
    // 获取用户信息、比对密码
    Result<UserDTO> result = loginFeignApi.login(loginDto,ip);
    if(ResultCode.SUCCESS.getCode()!=result.getCode()){
        log.error(result.getMsg());
        return result;
    }
    UserDTO user = result.getData();
    String token = JWTUtils.createJwt(user.getId() + "");
    data.put("token",token);
    return Result.success(data);
}

Among them, loginFeignApi.login(loginDto, ip) is  the interface to call the  user-service module through feign : query user information according to the user name, and perform MD5 encryption and salt verification on the password, as follows:

User user = userService.getUserByName(loginDTO.getUserName());
// 密码md5 校验
if (!user.getPassword().equals(MD5Utils.toMD5Password(user.getUserSalt(), loginDTO.getPassword()))){
    userService.userLog(user,ip,UserLogTypeEnum.LOGIN_FAIL,loginDTO.getSource());
    return Result.error(ResultCode.PASSWORD_ERROR);
}

2. gateway gateway service

In the same way, a gateway service module is constructed through SpringBoot, and the registration and configuration center uses Nacos uniformly, and configures the routing strategies of other service interfaces as a unified entry for other service APIs.

The gateway is responsible for verifying the token and putting user-related information into the header of the request so that the downstream system can easily obtain user information.

2.1 pom.xml  

Similarly, you need to introduce the jwt toolkit

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>${jwt.version}</version>
</dependency>

2.2 application.properties

server.port=1234
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

# gateway 路由策略
spring.cloud.gateway.routes[0].id=user
spring.cloud.gateway.routes[0].uri=lb://user-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/userService/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
spring.cloud.gateway.routes[1].id=order
spring.cloud.gateway.routes[1].uri=lb://order-service
spring.cloud.gateway.routes[1].predicates[0]=Path=/orderService/**
spring.cloud.gateway.routes[1].filters[0]=StripPrefix=1

### 。。。。。。。

2.3 Verify Token

We can implement the GatewayFilter  or  GlobalFilter filter interface. When the front-end request passes through the gateway, it will be filtered to verify the validity of the token. Here we implement the GlobalFilter interface.

TokenFilter.java

/**
 * @description: token过滤器
 * @author: xianhao_gan
 * @date:
 **/
@Slf4j
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtUtils jwtUtils;

    /**
     * 校验token
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String token = request.getHeaders().getFirst(RequestKeyConstants.TOKEN);
        //检查token是否为空
        if (StringUtils.isEmpty(token)) {
            return denyAccess(exchange, ResultCode.TOKEN_NULL);
        }

        Map claimMap1 = jwtUtils.parseJwt(token);
        //token有误
        if (claimMap.containsKey("exception")) {
            log.error() (claimMap1.get("exception").toString());
            return denyAccess(exchange, ResultCode.TOKEN_INVALID);
        }

        //token无误,将用户信息设置进header中,传递到下游服务
        Map<String, Claim> claimMap = claimMap1;
        String userId = claimMap.get(RequestKeyConstants.USER_ID).asString();
        Consumer<HttpHeaders> headers = httpHeaders -> {
            httpHeaders.add(RequestKeyConstants.USER_ID, userId);
        };
        request.mutate().headers(headers).build();
        
        // todo 权限校验

        return chain.filter(exchange);
    }

    /**
     * 拦截并返回自定义的json字符串
     */
    private Mono<Void> denyAccess(ServerWebExchange exchange, ResultCode resultCode) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        //这里在返回头添加编码,否则中文会乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] bytes = JSON.toJSONBytes(Result.error(resultCode), SerializerFeature.WriteMapNullValue);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -1;
    }

}

Note :

  • Of course, the code in the actual project will be more complicated than these. In order to facilitate the demonstration and explain the principle idea, part of the code is omitted. The actual design and development can be expanded according to this idea.

  • For tokens generated after login, the backend sets a fixed validity period. During the validity period, users carry the token for access. When the token expires, it will become invalid. The front end will jump to the login page to let the user log in again, but the user experience is not friendly.

  • Improvement: Active users should automatically obtain a new token after the token expires without being aware of it, and carry this new token for access, while users who have been inactive for a long time should log in again after the JWT expires. Later, there will be time to add the token timeout refresh strategy.

Reference: https://www.cnblogs.com/cjsblog/p/12425912.html

●The strongest Tomcat8 performance optimization in history

Why can Alibaba resist 10 billion in 90 seconds? --The evolution of server-side high-concurrency distributed architecture

B2B e-commerce platform--ChinaPay UnionPay electronic payment function

Learn Zookeeper distributed lock, let interviewers look at you with admiration

SpringCloud e-commerce spike microservice-Redisson distributed lock solution

Check out more good articles, enter the official account--please me--excellent in the past

A deep and soulful public account 0.0

 

Guess you like

Origin blog.csdn.net/a1036645146/article/details/109546416