Из коробки! Взгляните на их решение для разрешения микросервисов, оно называется элегантностью!

Java Spring Cloud
Из коробки! Взгляните на их решение для разрешения микросервисов, оно называется элегантностью!

Я помню статью, которую я написал раньшеSpring Cloud Gateway + Oauth2 — идеальное решение для разрешений микросервисов, реализующее унифицированную аутентификацию и аутентификацию!, предоставляет решение для разрешения в Spring Cloud. На самом деле, я не мог воспроизвести его в начале интеграции. Я проверил данные и посмотрел исходный код и, наконец, преуспел. Недавно я попробовал решение для разрешения микросервисов, предоставляемое Sa-Token, и оно кажется очень элегантным в использовании. Я рекомендую его всем!

Адрес фактического центра электронной коммерции SpringBoot (50k+star):GitHub.com/macro-positive/…

Предварительное знание

Мы будем использовать Nacos в качестве центра регистрации, Gateway в качестве шлюза и использовать решение для разрешения микросервисов, предоставляемое Sa-Token.Это решение основано на предыдущем решении.Друзья, которые не знакомы с этими технологиями, могут прочитать следующую статью.

Архитектура приложения

Это та же идея, что и в предыдущем решении: служба аутентификации отвечает за обработку входа в систему, шлюз отвечает за аутентификацию входа и аутентификацию полномочий, а другие службы API отвечают за обработку своей собственной бизнес-логики. Чтобы разделить сеансы Sa-Token между несколькими службами, все службы должны интегрировать Sa-Token и Redis.

  • micro-sa-token-common: общий инструментарий, пользовательский класс, общий для других сервисов.UserDTOи общий класс возвращаемого результатаCommonResultизвлечено здесь.
  • micro-sa-token-gateway: служба шлюза, отвечающая за переадресацию запросов, аутентификацию входа и авторизацию.
  • micro-sa-token-auth: служба аутентификации, включающая только один интерфейс входа в систему, который вызывает реализацию API Sa-Token.
  • micro-sa-token-api: защищенный сервис API, пользователи могут получить доступ к сервису после прохождения аутентификации на шлюзе.

Реализация схемы

Затем внедрите следующий набор решений и поочередно создайте службы шлюза, службы проверки подлинности и службы API.

micro-sa-token-gateway

Давайте сначала создадим службу шлюза, которая будет отвечать за аутентификацию входа и аутентификацию разрешений всего микросервиса.

  • В дополнение к общим зависимостям шлюза нам также необходимоpom.xmlДобавьте следующие зависимости вmicro-sa-token-commonполагаться;
<dependencies>
   <!-- Sa-Token 权限认证(Reactor响应式集成) -->
   <dependency>
       <groupId>cn.dev33</groupId>
       <artifactId>sa-token-reactor-spring-boot-starter</artifactId>
       <version>1.24.0</version>
   </dependency>
   <!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
   <dependency>
       <groupId>cn.dev33</groupId>
       <artifactId>sa-token-dao-redis-jackson</artifactId>
       <version>1.24.0</version>
   </dependency>
   <!-- 提供Redis连接池 -->
   <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-pool2</artifactId>
   </dependency>
   <!-- micro-sa-token通用依赖 -->
   <dependency>
       <groupId>com.macro.cloud</groupId>
       <artifactId>micro-sa-token-common</artifactId>
       <version>1.0.0</version>
   </dependency>
</dependencies>
  • Далее модифицируем конфигурационный файлapplication.yml, добавьте конфигурацию Redis и конфигурацию Sa-Token, если вы читали предыдущуюРуководство по использованию Sa-TokenЕсли это так, вы в основном знаете роль этих конфигураций;
spring:
  redis:
    database: 0
    port: 6379
    host: localhost
    password:

# Sa-Token配置
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: Authorization
  # token有效期,单位秒,-1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期),单位秒
  activity-timeout: -1
  # 是否允许同一账号并发登录 (为false时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个token (为false时每次登录新建一个token)
  is-share: false
  # token风格
  token-style: uuid
  # 是否输出操作日志
  is-log: false
  # 是否从cookie中读取token
  is-read-cookie: false
  # 是否从head中读取token
  is-read-head: true
  • Добавить класс конфигурации для Sa-TokenSaTokenConfig, введите фильтр для аутентификации входа и аутентификации разрешений, вsetAuthДобавьте правила маршрутизации в метод, вsetErrorДобавьте в метод обработку обратного вызова при сбое аутентификации;
@Configuration
public class SaTokenConfig {
    /**
     * 注册Sa-Token全局过滤器
     */
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截地址
                .addInclude("/**")
                // 开放地址
                .addExclude("/favicon.ico")
                // 鉴权方法:每次访问进入
                .setAuth(r -> {
                    // 登录认证:除登录接口都需要认证
                    SaRouter.match("/**", "/auth/user/login", StpUtil::checkLogin);
                    // 权限认证:不同接口访问权限不同
                    SaRouter.match("/api/test/hello", () -> StpUtil.checkPermission("api:test:hello"));
                    SaRouter.match("/api/user/info", () -> StpUtil.checkPermission("api:user:info"));
                })
                // setAuth方法异常处理
                .setError(e -> {
                    // 设置错误返回格式为JSON
                    ServerWebExchange exchange = SaReactorSyncHolder.getContent();
                    exchange.getResponse().getHeaders().set("Content-Type", "application/json; charset=utf-8");
                    return SaResult.error(e.getMessage());
                });
    }
}
  • Продлить предоставляемые Sa-TokenStpInterfaceИнтерфейс используется для получения разрешения пользователя.После того, как пользователь войдет в систему, мы будем хранить информацию о пользователе в сеансе, и информация о разрешении также будет там, поэтому код разрешения необходимо получить только из сеанса.
/**
 * 自定义权限验证接口扩展
 */
@Component
public class StpInterfaceImpl implements StpInterface {

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 返回此 loginId 拥有的权限码列表
        UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
        return userDTO.getPermissionList();
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 返回此 loginId 拥有的角色码列表
        return null;
    }

}

micro-sa-token-auth

Далее давайте создадим службу аутентификации, пока вы интегрируете Sa-Token и реализуете интерфейс входа в систему, это очень просто.

  • первый вpom.xmlДобавьте связанные зависимости, в том числе зависимость Sa-Token SpringBoot, интеграцию Redis для достижения распределенногоmicro-sa-token-commonполагаться;
<dependencies>
    <!-- Sa-Token 权限认证 -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-spring-boot-starter</artifactId>
        <version>1.24.0</version>
    </dependency>
    <!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-dao-redis-jackson</artifactId>
        <version>1.24.0</version>
    </dependency>
    <!-- 提供Redis连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <!-- micro-sa-token通用依赖 -->
    <dependency>
        <groupId>com.macro.cloud</groupId>
        <artifactId>micro-sa-token-common</artifactId>
        <version>1.0.0</version>
    </dependency>
</dependencies>
  • Далее модифицируем конфигурационный файлapplication.yml, скопируйте предыдущую конфигурацию шлюза;
spring:
  redis:
    database: 0
    port: 6379
    host: localhost
    password:

# Sa-Token配置
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: Authorization
  # token有效期,单位秒,-1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期),单位秒
  activity-timeout: -1
  # 是否允许同一账号并发登录 (为false时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个token (为false时每次登录新建一个token)
  is-share: false
  # token风格
  token-style: uuid
  # 是否输出操作日志
  is-log: false
  # 是否从cookie中读取token
  is-read-cookie: false
  # 是否从head中读取token
  is-read-head: true
  • существуетUserControllerИнтерфейс входа определен в , а токен возвращается после успешного входа в систему. Конкретная реализация находится вUserServiceImplкласс;
/**
 * 自定义Oauth2获取令牌接口
 * Created by macro on 2020/7/17.
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserServiceImpl userService;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public CommonResult login(@RequestParam String username, @RequestParam String password) {
        SaTokenInfo saTokenInfo = userService.login(username, password);
        if (saTokenInfo == null) {
            return CommonResult.validateFailed("用户名或密码错误");
        }
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", saTokenInfo.getTokenValue());
        tokenMap.put("tokenHead", saTokenInfo.getTokenName());
        return CommonResult.success(tokenMap);
    }
}
  • существуетUserServiceImplВ систему добавлена ​​особая логика входа в систему: сначала проверяется пароль, после успешной проверки пароля уведомляется идентификатор пользователя входа Sa-Token, а затем информация о пользователе сохраняется непосредственно в сеансе;
/**
 * 用户管理业务类
 * Created by macro on 2020/6/19.
 */
@Service
public class UserServiceImpl{

    private List<UserDTO> userList;

    public SaTokenInfo login(String username, String password) {
        SaTokenInfo saTokenInfo = null;
        UserDTO userDTO = loadUserByUsername(username);
        if (userDTO == null) {
            return null;
        }
        if (!SaSecureUtil.md5(password).equals(userDTO.getPassword())) {
            return null;
        }
        // 密码校验成功后登录,一行代码实现登录
        StpUtil.login(userDTO.getId());
        // 将用户信息存储到Session中
        StpUtil.getSession().set("userInfo",userDTO);
        // 获取当前登录用户Token信息
        saTokenInfo = StpUtil.getTokenInfo();
        return saTokenInfo;
    }
}
  • Здесь следует напомнить, что сеанс Sa-Token — это не HttpSession, который мы обычно понимаем, а механизм, подобный сеансу, реализованный сам по себе.

micro-sa-token-api

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

  • первый вpom.xmlДобавьте связанные зависимости в, и вышеmicro-sa-token-authТакой же;
<dependencies>
    <!-- Sa-Token 权限认证 -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-spring-boot-starter</artifactId>
        <version>1.24.0</version>
    </dependency>
    <!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-dao-redis-jackson</artifactId>
        <version>1.24.0</version>
    </dependency>
    <!-- 提供Redis连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <!-- micro-sa-token通用依赖 -->
    <dependency>
        <groupId>com.macro.cloud</groupId>
        <artifactId>micro-sa-token-common</artifactId>
        <version>1.0.0</version>
    </dependency>
</dependencies>
  • Далее модифицируем конфигурационный файлapplication.yml, скопируйте предыдущую конфигурацию шлюза;
spring:
  redis:
    database: 0
    port: 6379
    host: localhost
    password:

# Sa-Token配置
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: Authorization
  # token有效期,单位秒,-1代表永不过期
  timeout: 2592000
  # token临时有效期 (指定时间内无操作就视为token过期),单位秒
  activity-timeout: -1
  # 是否允许同一账号并发登录 (为false时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个token (为false时每次登录新建一个token)
  is-share: false
  # token风格
  token-style: uuid
  # 是否输出操作日志
  is-log: false
  # 是否从cookie中读取token
  is-read-cookie: false
  # 是否从head中读取token
  is-read-head: true
  • Добавьте интерфейс для получения информации о пользователе.Так как Redis используется для реализации распределенной сессии, то ее можно получить прямо из сессии.Не правда ли, очень просто!
/**
 * 获取登录用户信息接口
 * Created by macro on 2020/6/19.
 */
@RestController
@RequestMapping("/user")
public class UserController{

    @GetMapping("/info")
    public CommonResult<UserDTO> userInfo() {
        UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
        return CommonResult.success(userDTO);
    }

}
  • добавить потребностьapi:test:helloИнтерфейс проверки прав доступа, предустановленныйadminу пользователя есть это разрешение, иmacroНет пользователей.
/**
 * 测试接口
 * Created by macro on 2020/6/19.
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    public CommonResult hello() {
        return CommonResult.success("Hello World.");
    }

}

Демо

После создания трех служб мы используем Postman для демонстрации функции аутентификации и авторизации микрослужбы.

  • Сначала запустите службы Nacos и Redis, затем запуститеmicro-sa-token-gateway,micro-sa-token-authиmicro-sa-token-apiСервисы, порядок запуска не имеет значения;

  • Доступ к интерфейсу входа напрямую через шлюз для получения токена.Адрес доступа:http://localhost:9201/auth/user/login

  • Доступ к службам API через шлюзы,不带TokenДоступ к интерфейсу для получения информации о пользователе невозможен, адрес доступа:http://localhost:9201/api/user/info

  • Доступ к службам API через шлюзы,带TokenВызов интерфейса для получения информации о пользователе, доступ к которому возможен в обычном режиме;

  • Чтобы получить доступ к службе API через шлюз, используйтеmacroдоступ пользователяapi:test:helloИнтерфейс проверки разрешений, доступ к нему в обычном режиме невозможен, адрес доступа:http://localhost:9201/api/test/hello

  • логин переключиться наadminпользователь, пользователь имеетapi:test:helloорган власти;

  • Чтобы получить доступ к службе API через шлюз, используйтеadminПользователи могут получить доступ к тестовому интерфейсу в обычном режиме.

Суммировать

По сравнению с предыдущим решением для разрешений микросервисов, использующим Spring Security, решение Sa-Token проще и элегантнее. Чтобы использовать безопасность, нам нужно определить диспетчер аутентификации, иметь дело с ситуациями без аутентификации и несанкционированного доступа, а также самостоятельно определить аутентификацию и конфигурацию сервера ресурсов, что очень громоздко в использовании. С Sa-Token вам нужно только настроить фильтры на шлюзе для реализации аутентификации и авторизации, а затем вызвать API для реализации входа и назначения разрешений. Конкретная разница может относиться к рисунку ниже.

использованная литература

Официальная документация:sa-token.dev33.cn/

Адрес исходного кода проекта

GitHub.com/macro-positive/…