Вход для проверки токена JWT

JWT и токен

1. Что такое аутентификация по токену?

Аутентификация пользователя на основе токена — это метод аутентификации без сохранения состояния на стороне сервера . Так называемый метод без сохранения состояния на стороне сервера означает, что сам токен содержит все соответствующие данные вошедшего в систему пользователя, и каждый запрос клиента после аутентификации будет содержать токен. , поэтому на стороне сервера нет необходимости хранить данные токена.

После аутентификации пользователя сервер генерирует токен и отправляет его клиенту . Клиент может поместить его в файл cookie или localStorage и передавать токен с каждым запросом. Сервер может подтвердить личность пользователя после получения токена и передачи проверка.

2. Что такое JWT?

JSON Web Token (JWT) – это открытый отраслевой стандарт (RFC 7519), определяющий краткий, автономный формат протокола для передачи объектов JSON между взаимодействующими сторонами. Передаваемая информация может быть проверена и проверена с помощью цифровых подписей. 

3. Структура токена JWT  

Токен JWT состоит из трех частей: заголовка, полезных данных и подписи. Каждая часть разделена точкой (.), например: xxxxx.yyyyy.zzzzz. 

Заголовок 

Заголовок включает тип токена (т. е. JWT) и используемый алгоритм хеширования (т. е. HMAC, SHA256 или RSA).

// содержимое заголовка

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


// Используйте Base64Url для кодирования приведенного выше содержимого и получения строки, которая является первой частью токена JWT.

base64UrlEncode (заголовок)

Полезная нагрузка

Вторая часть — это полезная нагрузка. Содержимое также является объектом json. Это место для хранения достоверной информации. Он может хранить готовые поля, предоставленные jwt, такие как: iss (эмитент), exp (временная метка срока действия), sub (для пользователей) и т. д. Вы также можете настроить поля. Не рекомендуется хранить конфиденциальную информацию в этом разделе, поскольку этот раздел можно декодировать для восстановления исходного содержимого.

// содержимое полезной нагрузки

{     «sub»: «1234567890»,     «name»: «456»,     «admin»: true }



// Наконец, закодируйте вторую часть полезных данных с помощью Base64Url и получите строку, которая является второй частью токена JWT.

base64UrlEncode (полезная нагрузка)

Подпись

Третья часть — это подпись, которая используется для предотвращения подделки содержимого jwt. В этой части для кодирования первых двух частей используется base64url. После кодирования используйте точки (.) для соединения и формирования строки. Наконец, для подписи используйте алгоритм подписи, объявленный в заголовке.

// содержимое подписи

HMACSHA256(
base64UrlEncode(заголовок) + "." +
base64UrlEncode(полезная нагрузка),
секрет)

//Ключ, используемый для подписи

секрет

Токен реализует единый вход

Генерация и проверка токена JWT

класс инструмента jwt

public class AppJwtUtil {

  // 设置token过期时间30分钟
  private static final long EXPIRE_TIME = 30 * 60 * 1000; // 30分钟
  // 加密KEY
  private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";

  // 生产ID
  public static String getToken(Long id) {
    Map<String, Object> claimMaps = new HashMap<>();
    claimMaps.put("id", id);
    long currentTime = System.currentTimeMillis();
    return Jwts.builder()
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(new Date(currentTime)) // 签发时间
            .setSubject("system") // 说明
            .setIssuer("heima") // 签发者信息
            .setAudience("app") // 接收用户
            .compressWith(CompressionCodecs.GZIP) // 数据压缩方式
            .signWith(SignatureAlgorithm.HS512, TOKEN_ENCRY_KEY) // 加密方式
            .setExpiration(new Date(currentTime + EXPIRE_TIME)) // 过期时间戳
            .addClaims(claimMaps) // cla信息
            .compact();
  }

  /**
   * 获取payload body信息
   *
   * @param token
   * @return
   */
  public static Claims getClaimsBody(String token) {
    try {
      Jws<Claims> jwt = Jwts.parser().setSigningKey(TOKEN_ENCRY_KEY).parseClaimsJws(token);
      return jwt.getBody();
    } catch (Exception e) {
      return null;
    }
  }

  /**
   * 获取hearder body信息
   *
   * @param token
   * @return
   */
  public static JwsHeader getHeaderBody(String token) {
    Jws<Claims> jwt = Jwts.parser().setSigningKey(TOKEN_ENCRY_KEY).parseClaimsJws(token);
    return jwt.getHeader();
  }

  /**
   * 是否过期
   *
   * @param claims
   * @return 1:有效,0:无效
   */
  public static int verifyToken(Claims claims) {
    if (claims == null) {
      return 0;
    }
    // 当前时间在有效期范围内
    if(new Date().before(claims.getExpiration())){
      return 1;
    }
    return 0;
  }

}

Серверная часть использует проверку токена для входа в систему. 

Уровень обслуживания генерирует код токена

@Service
public class WmUserServiceImpl extends ServiceImpl<WmUserMapper, WmUser> implements WmUserService {

    @Override
    public ResponseResult login(WmLoginDto dto) {
        //1.检查参数
        if(StringUtils.isBlank(dto.getName()) || StringUtils.isBlank(dto.getPassword())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"用户名或密码为空");
        }

        //2.查询用户
        WmUser wmUser = getOne(Wrappers.<WmUser>lambdaQuery().eq(WmUser::getName, dto.getName()));
        if(wmUser == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //3.比对密码
        String loginPwd = dto.getPassword(); //明文密码
        String dbPwd = wmUser.getPassword(); //密文密码

        boolean result = BCrypt.checkpw(loginPwd, dbPwd);

        if(result){
            //4.返回数据  jwt
            Map<String,Object> map  = new HashMap<>();
            map.put("token", AppJwtUtil.getToken(wmUser.getId().longValue()));
            wmUser.setSalt("");
            wmUser.setPassword("");
            map.put("user",wmUser);
            return ResponseResult.okResult(map);

        }else {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
        }
    }
}

Код токена проверки перехватчика 

@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //2.判断是否是登录接口,是直接放行
        String path = request.getURI().getPath();
        if(path.equals("/user/api/v1/login/login_auth/")){
            return chain.filter(exchange);
        }

        //3.非登录接口,获取token
        String token = request.getHeaders().getFirst("token");


        //4.判断token是否存在,不存在返回401(无权访问)
        if(StringUtils.isBlank(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }


        //5.判断token是否有效,无效返回401(无权访问)
        Claims claimsBody = AppJwtUtil.getClaimsBody(token);
        try {

            int i = AppJwtUtil.verifyToken(claimsBody);
            if(i==0){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //从token载荷里获取用户ID
        String userId = String.valueOf(claimsBody.get("id"));
        //将用户ID设置到请求头
        request.mutate().header("userId",userId);


        //6.放行
        return chain.filter(exchange);
    }

    /**
     * 优先级设置  值越小  优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

Предотвращение подделки токенов 

  1. Как показано в приведенном выше коде, идентификатор пользователя сохраняется при создании токена, а при повторном запросе идентификатор пользователя, содержащийся в запросе, сравнивается с идентификатором пользователя, анализируемым в теле токена, чтобы определить, совпадают ли они.
  2. При создании токена сохраните запрошенный IP-адрес. При повторном запросе сравните, соответствует ли текущий запрошенный IP-адрес IP-адресу, проанализированному в токене. 

Guess you like

Origin blog.csdn.net/m0_69057918/article/details/132391212
jwt