Перцептивное понимание JWT

задняя часть Микросервисы сервер открытый источник

Я давно не вел блог, потому что недавно компания попросила меня научитьсяspring cloud, заблаговременно перенесите предыдущее программное обеспечение на новую архитектуру. Так что я усердно учусь, всегда стараюсь быть быстрым, многие ключевые ноты записаны плохо, а сейчас я забыл многие ключевые технические моменты, что крайне грешно!

Размышляя об этом сейчас, лучше пойти по приземленному пути. Нет, сегодня я взял пузырек, чтобы компенсировать выученные и забытые очки знаний.

Хотите разблокировать больше новых поз? Пожалуйста, посетитемой блог.

Общие механизмы аутентификации

Сегодня я расскажу о JWT.

Что касается JWT, я думаю, что многие люди видели и использовали его.jsonПроще говоря, спецификация аутентификации структуры данных заключается в проверке того, вошел ли пользователь в систему или нет. В это время вы можете вспомнить, о, это снова не сеанс, распределенная система используетredisКаковы преимущества этого jwt для распределенных сеансов?

Пожалуйста, послушай, как я медленно расскажу эту историю!

Самый примитивный способ - HTTP BASIC AUTH

HTTP BASIC auth, не смотрите на его название так долго и так долго, вы думаете, что эта штука очень высокая. На самом деле принцип очень прост, проще говоря, каждый раз при запросе API будет передаваться логин и пароль.restful APIпередан на сервер. Это позволяет достичьнет статусаИдея состоит в том, что каждый HTTP-запрос не имеет ничего общего с предыдущим, просто получите целевой URI, и после получения целевого контента соединение будет уничтожено без каких-либо следов. Не кажется ли вам, что безгражданство — это горячая идея прямо сейчас, и вы думаете, что это очень мощная идея. На самом деле его недостатки еще в другом.Когда мы отправляем HTTP-запрос на сервер, очень вероятно, что наш логин и пароль будут напрямую выставлены стороннему клиенту, что очень рискованно.Поэтому этот метод редко используется используется в производственной среде.

Сессия и куки

Сеансы и куки — обычное дело. Вначале на стороне сервера будет глобально создан объект сеанса, который хранит различную ключевую информацию и отправляет наборsessionId, который становится объектом cookie, хранящимся в браузере.

Во время аутентификации данные файла cookie будут переданы на сервер для сопоставления с сеансом, а затем будет выполнена аутентификация данных.

how session work

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

недостаток

Этот метод аутентификации в настоящее время является наиболее часто используемым методом для программного обеспечения, и у него есть некоторые собственные недостатки:

  • безопасность. Безопасность куки-файлов не очень хорошая, злоумышленники могут обмануть, получив локальные куки-файлы или используя куки-файлы дляCSRFатака.
  • междоменная проблема. При использовании файлов cookie могут возникать междоменные проблемы с несколькими доменными именами.
  • состояние. Сессию необходимо хранить на сервере в течение определенного периода времени, поэтому при большом количестве пользователей производительность сервера также будет сильно снижаться.
  • проблема со статусом. Когда есть несколько машин, то как разделить сеанс также будет проблемой.То есть, когда пользователь впервые обращается к серверу A, а второй запрос перенаправляется на сервер B, как сервер B узнает о своем состоянии.
  • проблема с мобильным телефоном. Современные смартфоны, в том числе Android, изначально не поддерживают файлы cookie, и использовать файлы cookie довольно проблематично.

Аутентификация токена (с использованием спецификации jwt)

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

  1. Клиент запрашивает логин с логином и паролем
  2. Сервер получает запрос на проверку имени пользователя и пароля
  3. После успешной проверки сервер выдаст токен, а затем отправит токен клиенту.
  4. После того, как клиент получит токен, его можно сохранить, например, в файле cookie.
  5. Каждый раз, когда клиент запрашивает ресурсы с сервера, ему необходимо принести токен, выданный сервером.
  6. Сервер получает запрос, а затем проверяет токен, переданный в клиентском запросе.Если проверка прошла успешно, он возвращает запрошенные данные клиенту.

Механизм Token, я думаю, что его основная идея заключается в том, чтобы сильно упростить информацию в сеансе и использовать его как файл cookie, то есть «сеанс» клиента.

выгода

В чем разница между механизмом Token и механизмом Cookie?выгодаШерстяная ткань?

  • Поддержка междоменного доступа:Cookie не разрешает доступ с неработающих доменов, которых не существует для механизма Token, при условии, что передаваемая информация аутентификации пользователя передается через HTTP-заголовки.
  • нет статусаСуть механизма :Token заключается в верификации, а состояние сеанса, которое он получает, полностью поступает со стороны клиента.Механизму Token не нужно хранить информацию о сеансе на стороне сервера, потому что сам токен содержит информацию обо всех авторизованных пользователях. пользователей, и его нужно хранить только в файле cookie клиента или локальном носителе.Хранить информацию о состоянии.
  • Больше подходит для CDN: вы можете запросить все данные на вашем сервере (такие как javascript, HTML, изображения и т. д.) через сеть распространения контента, и ваш сервер должен только предоставить API.
  • разъединение: не нужно привязываться к конкретной схеме аутентификации. Токены могут быть сгенерированы где угодно, если вы можете сделать вызов генерации токена при вызове вашего API.
  • Больше подходит для мобильных приложений: Когда ваш клиент является нативной платформой (iOS, Android, Windows 8 и т. д.), файлы cookie не поддерживаются (обрабатывать это нужно через контейнер Cookie), то гораздо проще использовать механизм аутентификации Token. CSRF: вам не нужно беспокоиться о защите CSRF (подделка межсайтовых запросов), потому что вы больше не полагаетесь на файлы cookie.
  • представление: Время прохождения сети (запрос информации о сеансе через базу данных) занимает гораздо больше времени, чем проверка токена и анализ для расчета HMACSHA 256. Для страницы входа не требуется специальной обработки: если вы используете Protractor для функционального тестирования, больше не требуется Выполнять специальную обработку страниц входа.
  • на основе стандартизации: ваш API может использовать стандартизированный веб-токен JSON (JWT). Этот стандарт уже существует во многих серверных библиотеках (.NET, Ruby, Java, Python, PHP) и поддерживается несколькими компаниями (например, Firebase, Google, Microsoft).

Где недостатки?

Он сказал так много преимуществ аутентификации токенов, но он не так хорош, как он себе представлял, и токены не лишены проблем.

  1. Занять пропускную способность

    В обычных обстоятельствах он больше, чем session_id, и его необходимопотреблять больше трафика, выжать больше пропускной способности, если у вашего сайта 100 000 браузеров в месяц, значит, вам нужно тратить десятки мегабайт трафика. Это не звучит как много, но это много денег с течением времени. На самом деле многие люди будут хранить больше информации в JWT.

  2. В любом случае вам нужно манипулировать базой данных

    При использовании JWT на веб-сайте практически для всех страниц, загружаемых пользователем, необходимо загрузить информацию о пользователе из кеша/базы данных. Вы уверены, что эта операция подходит для высоконагруженных сервисов? Если для кэширования используется Redis, он не более эффективен, чем сеанс.

  3. Невозможно выйти на сервер, так долгоТрудно решить угонпроблема

  4. проблемы с производительностью

    Одним из преимуществ JWT являются криптографические подписи, благодаря которым получатель может убедиться, что JWT действителен и надежен. Но в большинстве приложений веб-аутентификации JWT хранится в файле cookie, что означает наличие двух уровней подписей. Звучит круто, но преимущества нет, и за это вы платите вдвое больше ресурсов ЦП за проверку подписи. Это не идеально для веб-приложений с жесткими требованиями к производительности, особенно для однопоточных сред.

JWT

Теперь давайте поговорим о главном герое сегодняшнего дня,JWT

JSON Web Token (JWT) — очень легкая спецификация. Эта спецификация позволяет нам использовать JWT для безопасной и надежной передачи информации между пользователем и сервером.

1543760350545

сочинение

JWT на самом деле представляет собой строку, состоящую из трех частей:голова,нагрузкаиподписать.

заголовок

Заголовок используется для описания самой основной информации о JWT, такой как его тип и алгоритм, используемый для его подписи. Это также может быть представлено как объект JSON.

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

Это открытый текст заголовка.Первая часть указывает на то, что он jwt, а вторая часть указывает на то, что алгоритм подписи используетАлгоритм HS256.

Этот заголовок затем кодируется BASE64 и кодируется для формирования заголовка:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

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

Полезная нагрузка — это место, где хранится достоверная информация, а достоверная информация состоит из трех частей:

(1)Декларация, зарегистрированная в стандарте(Предложить, но не заставить)

  • iss: эмитент jwt
  • sub: пользователи для jwt
  • aud: сторона, получающая jwt
  • exp: время истечения jwt, которое должно быть больше, чем время выдачи
  • nbf: определяет, до какого времени jwt недоступен.
  • iat: время выпуска jwt
  • jti: уникальный идентификатор jwt, который в основном используется как одноразовый токен, чтобы избежать повторных атак.

(2)публичное заявлениеПубличное заявление может добавлять любую информацию, как правило, информацию о пользователе или другую необходимую информацию для нужд бизнеса, но не рекомендуется добавлять конфиденциальную информацию, потому что эта часть может быть расшифрована на стороне клиента.

(3)личное заявление

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

{
    "sub":"1234567890",
    "name":"tengshe789",
    "admin": true
}

Выше приведен открытый текст простой полезной нагрузки, затем зашифрованной с помощью base64:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

Виза (подпись)

Третья часть jwt — это визовая информация, которая состоит из трех частей:

  1. заголовок (после base64)
  2. полезная нагрузка (после base64)
  3. secret

Эта часть требует использования заголовка, зашифрованного с помощью base64, и полезной нагрузки, зашифрованной с помощью base64.Строка, сформированная соединением, затем шифруется с помощью метода шифрования, объявленного в заголовке, с комбинацией секретов с солью, которая затем составляет третью часть jwt.

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

синтез

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6I kpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7Hg Q

Внедрить JWT

В настоящее время JWT обычно реализуется с использованием проекта Apache с открытым исходным кодом JJWT (библиотека Java, обеспечивающая сквозное создание и проверку JWT).

полагаться

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>

Создайте демо-версию токена

public class CreateJWT {
    public static void main(String[] args) throws Exception{
        JwtBuilder builder = Jwts.builder().setId("123")
                .setSubject("jwt所面向的用户")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256,"tengshe789");
        String s = builder.compact();
        System.out.println(s);
        //eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiJqd3TmiYDpnaLlkJHnmoTnlKjmiLciLCJpYXQiOjE1NDM3NTk0MjJ9.1sIlEynqqZmA4PbKI6GgiP3ljk_aiypcsUxSN6-ATIA
    }
}

Результат выглядит следующим образом:

1543759471279

(Обратите внимание, что jjwt не поддерживает jdk11, и jjwt после 0.9.1 должен реализовать метод signWith() для достижения этого)

Разобрать демо Token

public class ParseJWT {
    public static void main(String[] args) {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiJqd3TmiYDpnaLlkJHnmoTnlKjmiLciLCJpYXQiOjE1NDM3NTk0MjJ9.1sIlEynqqZmA4PbKI6GgiP3ljk_aiypcsUxSN6-ATIA";

        Claims claims =
                Jwts.parser().setSigningKey("tengshe789").parseClaimsJws(token).getBody();
        
        System.out.println("id"+claims.getId());
        System.out.println("Subject"+claims.getSubject());
        System.out.println("IssuedAt"+claims.getIssuedAt());
    }
}

Результат выглядит следующим образом:

1543759769057

JWT в производстве

В системах корпоративного уровня обычно существует множество внутренних инструментальных платформ, которые могут использовать все, например, управление персоналом, управление кодом, мониторинг журналов, бюджетное приложение и т. д. Если каждая платформа реализует свою собственную пользовательскую систему, это, несомненно, будет огромным расточительством, поэтому внутри компании будет набор общедоступных пользовательских систем, и пользователи могут получить доступ ко всем системам, пока они входят в систему.

ЭтоЕдиный вход (SSO: единый вход)

SSO — это общий термин для класса решений, и с точки зрения конкретной реализации обычно есть две стратегии на выбор:

  1. SAML 2.0
  2. OAuth 2.0

Если вы хотите сначала продвигаться, давайте сначала поговорим о нескольких важных моментах знаний.

Authentication VS Authorisation

  • Аутентификация: аутентификация личности, аутентификация, в дальнейшем именуемая аутентификацией

    СертификацияРоль заключается в том, чтобы признать, что у вас есть разрешение на доступ к системе, чтобы определить, является ли посетитель законным пользователем. Служба, отвечающая за аутентификацию, обычно называется сервером авторизации или поставщиком удостоверений, далее именуемымIdP

  • Авторизация: Авторизация

    РазрешитьИспользуется для определения того, к каким ресурсам у вас есть доступ. Большинство людей не видят разницы между ними, потому что они на стороне пользователя. Как разработчик системы, между ними есть различия.Это две разные должностные обязанности.Нам может понадобиться только функция аутентификации, но не функция авторизации, или даже реализовать функцию аутентификации самостоятельно.С помощью Google Аутентификация системы, то есть пользователь может войти в систему с помощью учетной записи Google. Служба, отвечающая за предоставление ресурсов (вызовы API), называется Resource Server или Service Provider, далее именуемаяSP

SMAL 2.0

smal flow

ОАут (JWT)

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

Процесс может относиться к следующему:

oauth

Проще говоря, если вы хотите получить доступ к сервису приложения, сначала найдите его и запроситеrequest token(токен запроса), затем поместите этоrequest tokenОтправьте его на сторонний сервер аутентификации, и сторонний сервер аутентификации предоставит вамaceess token(проходной жетон), сaceess tokenВы можете использовать службу приложений.

Обратите внимание на преобразование в шаге 4 на картинкеaccess tokenпроцесс, многие сторонние системы, такие как Google, не просто возвращаютaccess token, также вернет дополнительную информацию, связанную с последующими обновлениями.refresh token. однаждыaccess tokenистек срок, вы можете пройтиrefresh tokenзапросить сноваaccess token.

refresh token

Конечно, процесс зависит от вашего метода запроса и типа ресурсов, к которым вы обращаетесь.Многие бизнесы тоже разные.Я просто говорю об этом.

Теперь этот метод более распространен, например, использование QQ для быстрого входа в систему, и этот метод в основном используется.

проект с открытым исходным кодом

Мы используем очень популярный проект с открытым исходным кодомCloud-AdminДля каштанов давайте проанализируем применение jwt.

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

Структура каталогов

1543763543823

Функция центра аутентификации находится вace-authиace-gateВниз.

Модель

Ниже представлена ​​официально предоставленная модель архитектуры.

image.png

можно увидеть,AuthServerВ центральной части архитектуры для доступа к сервису должна требоваться JWT-аутентификация центра аутентификации.

Интерпретация кода сервера центра аутентификации

класс сущности

Сначала взгляните на класс сущностей, где центр аутентификации определяет набор клиентских сущностей следующим образом:

@Table(name = "auth_client")
@Getter
@Setter
public class Client {
    @Id
    private Integer id;

    private String code;

    private String secret;

    private String name;

    private String locked = "0";

    private String description;

    @Column(name = "crt_time")
    private Date crtTime;

    @Column(name = "crt_user")
    private String crtUser;

    @Column(name = "crt_name")
    private String crtName;

    @Column(name = "crt_host")
    private String crtHost;

    @Column(name = "upd_time")
    private Date updTime;

    @Column(name = "upd_user")
    private String updUser;

    @Column(name = "upd_name")
    private String updName;

    @Column(name = "upd_host")
    private String updHost;
    
    private String attr1;
    private String attr2;
    private String attr3;
    private String attr4;
    private String attr5;
    private String attr6;
    private String attr7;
    private String attr8;

Соответствующая база данных:

CREATE TABLE `auth_client` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(255) DEFAULT NULL COMMENT '服务编码',
  `secret` varchar(255) DEFAULT NULL COMMENT '服务密钥',
  `name` varchar(255) DEFAULT NULL COMMENT '服务名',
  `locked` char(1) DEFAULT NULL COMMENT '是否锁定',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `crt_time` datetime DEFAULT NULL COMMENT '创建时间',
  `crt_user` varchar(255) DEFAULT NULL COMMENT '创建人',
  `crt_name` varchar(255) DEFAULT NULL COMMENT '创建人姓名',
  `crt_host` varchar(255) DEFAULT NULL COMMENT '创建主机',
  `upd_time` datetime DEFAULT NULL COMMENT '更新时间',
  `upd_user` varchar(255) DEFAULT NULL COMMENT '更新人',
  `upd_name` varchar(255) DEFAULT NULL COMMENT '更新姓名',
  `upd_host` varchar(255) DEFAULT NULL COMMENT '更新主机',
  `attr1` varchar(255) DEFAULT NULL,
  `attr2` varchar(255) DEFAULT NULL,
  `attr3` varchar(255) DEFAULT NULL,
  `attr4` varchar(255) DEFAULT NULL,
  `attr5` varchar(255) DEFAULT NULL,
  `attr6` varchar(255) DEFAULT NULL,
  `attr7` varchar(255) DEFAULT NULL,
  `attr8` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;

Это информация для каждой группы клиентов микросервиса.

Второй класс сущностей — это сущность client_service, которая соответствует тому, какие клиенты микрослужбы могут вызываться этими клиентами микрослужб:

Вероятно, соответствует отношениям разрешения вызовов между микросервисами.

@Table(name = "auth_client_service")
public class ClientService {
    @Id
    private Integer id;

    @Column(name = "service_id")
    private String serviceId;

    @Column(name = "client_id")
    private String clientId;

    private String description;

    @Column(name = "crt_time")
    private Date crtTime;

    @Column(name = "crt_user")
    private String crtUser;

    @Column(name = "crt_name")
    private String crtName;

    @Column(name = "crt_host")
    private String crtHost;}

интерфейсный слой

Давайте прыгнем и посмотрим, сначала посмотрим на слой интерфейса

@RestController
@RequestMapping("jwt")
@Slf4j
public class AuthController {
    @Value("${jwt.token-header}")
    private String tokenHeader;

    @Autowired
    private AuthService authService;

    @RequestMapping(value = "token", method = RequestMethod.POST)
    public ObjectRestResponse<String> createAuthenticationToken(
            @RequestBody JwtAuthenticationRequest authenticationRequest) throws Exception {
        log.info(authenticationRequest.getUsername()+" require logging...");
        final String token = authService.login(authenticationRequest);
        return new ObjectRestResponse<>().data(token);
    }

    @RequestMapping(value = "refresh", method = RequestMethod.GET)
    public ObjectRestResponse<String> refreshAndGetAuthenticationToken(
            HttpServletRequest request) throws Exception {
        String token = request.getHeader(tokenHeader);
        String refreshedToken = authService.refresh(token);
        return new ObjectRestResponse<>().data(refreshedToken);
    }

    @RequestMapping(value = "verify", method = RequestMethod.GET)
    public ObjectRestResponse<?> verify(String token) throws Exception {
        authService.validate(token);
        return new ObjectRestResponse<>();
    }
}

Вот три интерфейса

Поговорим о первом интерфейсе, создаемtoken.

Конкретная логика выглядит следующим образом: каждый пользовательавторизоватьсяКогда вы войдете, вы войдете в эту ссылку. В соответствии с именем пользователя и паролем пользователя в запросе используйтеfeignПерехватчик клиента перехватывает запрос, а затем использует авторскийJwtTokenUtilРазличные методы внутри извлекают ключ и ключ в токене и проверяют правильность токена.Если он правильный, используйтеauthService.login(authenticationRequest);Метод возвращает новый токен.

public String login(JwtAuthenticationRequest authenticationRequest) throws Exception {
        UserInfo info = userService.validate(authenticationRequest);
        if (!StringUtils.isEmpty(info.getId())) {
            return jwtTokenUtil.generateToken(new JWTInfo(info.getUsername(), info.getId() + "", info.getName()));
        }
        throw new UserInvalidException("用户不存在或账户密码错误!");
    }

На следующем рисунке представлена ​​подробная логическая схема:

model

Код клиента центра аутентификации

Вход

Автор написал аннотацию, используя@EnableAceAuthClientТо есть управление аутентификацией микросервисов (клиентов) включается автоматически.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AutoConfiguration.class)
@Documented
@Inherited
public @interface EnableAceAuthClient {
}

настроить

Затем посмотрите вдоль входа аннотации

@Configuration
@ComponentScan({"com.github.wxiaoqi.security.auth.client","com.github.wxiaoqi.security.auth.common.event"})
public class AutoConfiguration {
    @Bean
    ServiceAuthConfig getServiceAuthConfig(){
        return new ServiceAuthConfig();
    }
    @Bean
    UserAuthConfig getUserAuthConfig(){
        return new UserAuthConfig();
    }
}

Аннотация автоматически загрузит ключевую информацию о клиентском токене пользователя и сервисном токене в bean-компонент.

фейгин перехватчик

автор переписываетokhttp3Метод перехватчика каждый раз, когда токен, запрошенный клиентом микрослужбы, будет перехватываться, проверяет, истек ли срок действия токена службы, вызывающей службу, и токена пользователя, вызывающего службу, и после истечения срока действия будет возвращен новый токен.

@Override
    public Response intercept(Chain chain) throws IOException {
        Request newRequest = null;
        if (chain.request().url().toString().contains("client/token")) {
            newRequest = chain.request()
                    .newBuilder()
                    .header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
                    .build();
        } else {
            newRequest = chain.request()
                    .newBuilder()
                    .header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
                    .header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken())
                    .build();
        }
        Response response = chain.proceed(newRequest);
        if (HttpStatus.FORBIDDEN.value() == response.code()) {
            if (response.body().string().contains(String.valueOf(CommonConstants.EX_CLIENT_INVALID_CODE))) {
                log.info("Client Token Expire,Retry to request...");
                serviceAuthUtil.refreshClientToken();
                newRequest = chain.request()
                        .newBuilder()
                        .header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
                        .header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken())
                        .build();
                response = chain.proceed(newRequest);
            }
        }
        return response;
    }

Перехватчик для пружинного контейнера

Второй перехватчик из контейнера spring.Первый перехватчик только проверяет, истек ли срок действия двух токенов, но реальные разрешения токенов не проверяются. Следующим шагом является проверка разрешений двух токенов.

Код разрешения вызова службы выглядит следующим образом:

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 配置该注解,说明不进行服务拦截
        IgnoreClientToken annotation = handlerMethod.getBeanType().getAnnotation(IgnoreClientToken.class);
        if (annotation == null) {
            annotation = handlerMethod.getMethodAnnotation(IgnoreClientToken.class);
        }
        if(annotation!=null) {
            return super.preHandle(request, response, handler);
        }

        String token = request.getHeader(serviceAuthConfig.getTokenHeader());
        IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
        String uniqueName = infoFromToken.getUniqueName();
        for(String client:serviceAuthUtil.getAllowedClient()){
            if(client.equals(uniqueName)){
                return super.preHandle(request, response, handler);
            }
        }
        throw new ClientForbiddenException("Client is Forbidden!");
    }

Права пользователя:

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 配置该注解,说明不进行用户拦截
        IgnoreUserToken annotation = handlerMethod.getBeanType().getAnnotation(IgnoreUserToken.class);
        if (annotation == null) {
            annotation = handlerMethod.getMethodAnnotation(IgnoreUserToken.class);
        }
        if (annotation != null) {
            return super.preHandle(request, response, handler);
        }
        String token = request.getHeader(userAuthConfig.getTokenHeader());
        if (StringUtils.isEmpty(token)) {
            if (request.getCookies() != null) {
                for (Cookie cookie : request.getCookies()) {
                    if (cookie.getName().equals(userAuthConfig.getTokenHeader())) {
                        token = cookie.getValue();
                    }
                }
            }
        }
        IJWTInfo infoFromToken = userAuthUtil.getInfoFromToken(token);
        BaseContextHandler.setUsername(infoFromToken.getUniqueName());
        BaseContextHandler.setName(infoFromToken.getName());
        BaseContextHandler.setUserID(infoFromToken.getId());
        return super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        BaseContextHandler.remove();
        super.afterCompletion(request, response, handler, ex);
    }

Весенний код шлюза облачного шлюза

Все запросы в этой структуре будут проходить через службу шлюза (ace-gatev2) через шлюз, чтобы проверить, не истек ли срок действия токена ненормально, проверить, не существует ли токен, и проверить, есть ли у токена разрешение на обслуживание.

Вот основной код:

@Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) {
        log.info("check token and user permission....");
        LinkedHashSet requiredAttribute = serverWebExchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        ServerHttpRequest request = serverWebExchange.getRequest();
        String requestUri = request.getPath().pathWithinApplication().value();
        if (requiredAttribute != null) {
            Iterator<URI> iterator = requiredAttribute.iterator();
            while (iterator.hasNext()){
                URI next = iterator.next();
                if(next.getPath().startsWith(GATE_WAY_PREFIX)){
                    requestUri = next.getPath().substring(GATE_WAY_PREFIX.length());
                }
            }
        }
        final String method = request.getMethod().toString();
        BaseContextHandler.setToken(null);
        ServerHttpRequest.Builder mutate = request.mutate();
        // 不进行拦截的地址
        if (isStartWith(requestUri)) {
            ServerHttpRequest build = mutate.build();
            return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
        }
        IJWTInfo user = null;
        try {
            user = getJWTUser(request, mutate);
        } catch (Exception e) {
            log.error("用户Token过期异常", e);
            return getVoidMono(serverWebExchange, new TokenForbiddenResponse("User Token Forbidden or Expired!"));
        }
        List<PermissionInfo> permissionIfs = userService.getAllPermissionInfo();
        // 判断资源是否启用权限约束
        Stream<PermissionInfo> stream = getPermissionIfs(requestUri, method, permissionIfs);
        List<PermissionInfo> result = stream.collect(Collectors.toList());
        PermissionInfo[] permissions = result.toArray(new PermissionInfo[]{});
        if (permissions.length > 0) {
            if (checkUserPermission(permissions, serverWebExchange, user)) {
                return getVoidMono(serverWebExchange, new TokenForbiddenResponse("User Forbidden!Does not has Permission!"));
            }
        }
        // 申请客户端密钥头
        mutate.header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken());
        ServerHttpRequest build = mutate.build();
        return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());

    }

1543848104059

cloud adminСводка

В общем, модули аутентификации и шлюза готовы. Концепция кода автора очень тонкая.При использовании в крупномасштабной системе разрешений она может незначительно уменьшить связанность, сделать аутентификацию службы более тонкой и упростить управление.

Заканчивать

Этот фильм закончился~ Хотите узнать больше о новых захватывающих позах?
пожалуйста, посетите мойличный блог

Эта статья является оригинальным содержанием, уже вличный блогПервая публикация, а затем настроение может быть выпущено одновременно на CSDN, segmentfault, Nuggets, Jianshu и Open Source China. Если есть сходство,судьбародной брат. Спешите добавить друга, давайте придумаем число, купим лотерейный билет и заработаем ему несколько миллионов первым😝