当前位置:   article > 正文

【Sa-Token|4】Sa-Token微服务项目应用_sa-token生成 接口token

sa-token生成 接口token

微服务数量多,如果每个服务都改动,工作量大,则可以只在网关和用户中心进行改动,也是可以实现服务之间的跳转。
这种方式可以通过在网关服务中生成和验证 Sa-Token,并将其与现有的 Token关联存储在 Redis 中。用户中心提供额外的接口来验证和生成新的 Sa-Token。
这样只需要改造网关和用户服务,其他服务保持现状,即可实现各个应用之间的跳转

在这里插入图片描述

实现方案

为了实现这个目标,同时尽量减少对现有系统的改动,可以在网关和用户中心进行必要的改动,以确保用户在登录后能够在多个系统中无缝访问。

下面是详细的方案和步骤:

一、用户中心改造

用户中心需要提供两个接口:

  1. 登录接口:用户登录并生成原有业务系统的 Token。
  2. Token 验证和 Sa-Token 生成接口:验证原有 Token 并生成 Sa-Token。
1. 登录接口
@RestController
@RequestMapping("/auth")
public class AuthController {

    @PostMapping("/login")
    public ResponseEntity<Map<String, Object>> login(@RequestParam String username, @RequestParam String password) {
        Map<String, Object> result = new HashMap<>();
        // 进行用户名和密码校验
        if (checkCredentials(username, password)) {
            String originalToken = generateOriginalToken(username); // 生成原有业务系统的 Token
            result.put("originalToken", originalToken);
            return ResponseEntity.ok(result);
        }
        result.put("error", "登录失败");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(result);
    }

    private boolean checkCredentials(String username, String password) {
        // 检查用户名和密码逻辑
        return true; // 假设校验成功
    }

    private String generateOriginalToken(String username) {
        // 生成原有业务系统的 Token
        return "original-token";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
2. Token 验证和 Sa-Token 生成接口
@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/check")
    public ResponseEntity<SaResult> checkToken(@RequestParam String token) {
        // 验证原有 Token
        if (validateOriginalToken(token)) {
            String userId = getUserIdFromOriginalToken(token);
            StpUtil.login(userId);
            String saToken = StpUtil.getTokenValue();
            // 存储原有 Token 和 Sa-Token 的映射关系
            redisTemplate.opsForValue().set("sa-token:" + token, saToken);
            redisTemplate.opsForValue().set("original-token:" + saToken, token);
            return ResponseEntity.ok(SaResult.ok("Token 有效").set("saToken", saToken));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(SaResult.error("Token 无效"));
    }

    private boolean validateOriginalToken(String token) {
        // 验证原有业务系统的 Token
        return true; // 假设校验成功
    }

    private String getUserIdFromOriginalToken(String token) {
        // 从原有业务系统的 Token 中解析出用户ID
        return "parsed-user-id";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

二、网关服务改造

网关服务需要在所有请求到达后端服务前,进行原有 Token 的验证和 Sa-Token 的生成和验证。

1. 引入 Redis 依赖

确保网关服务的 pom.xml 文件中包含 Redis 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
2. 配置 Redis

application.yml 文件中配置 Redis 连接信息:

spring:
  redis:
    host: localhost
    port: 6379
    password: ""
  • 1
  • 2
  • 3
  • 4
  • 5
3. 网关服务 Token 校验过滤器
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String originalToken = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (originalToken != null) {
            String saToken = redisTemplate.opsForValue().get("sa-token:" + originalToken);
            if (saToken == null) {
                // 调用用户中心的接口生成 Sa-Token
                saToken = generateSaToken(originalToken);
                if (saToken == null) {
                    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                    return exchange.getResponse().setComplete();
                }
            }
            exchange.getRequest().mutate().header("Sa-Token", saToken).build();
            return chain.filter(exchange);
        }
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

    private String generateSaToken(String originalToken) {
        // 调用用户中心的接口生成 Sa-Token
        String url = "http://user-center/auth/check?token=" + originalToken;
        ResponseEntity<SaResult> response = restTemplate.getForEntity(url, SaResult.class);
        if (response.getStatusCode() == HttpStatus.OK && response.getBody().getCode() == 200) {
            return response.getBody().get("saToken").toString();
        }
        return null;
    }

    @Override
    public int getOrder() {
        return -100;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

三、工作流程

  1. 用户登录

    • 用户通过用户中心的登录接口进行登录,用户中心生成原有业务系统的 Token 并返回给前端。
  2. 请求网关

    • 前端在请求其他服务时,将原有的 Token 添加到请求头中,发送请求到网关。
  3. 网关处理

    • 网关拦截请求,从请求头中获取原有的 Token。
    • 网关调用用户中心的 Token 验证接口来验证原有 Token 并生成 Sa-Token。
    • 将原有 Token 和 Sa-Token 的映射关系存储在 Redis 中。
    • 将 Sa-Token 添加到请求头中,转发请求到后端服务。
  4. 后端服务

    • 后端服务可以继续保持现有的 Token 逻辑,不需要改动。所有的 Token 验证和生成逻辑都在网关和用户中心处理。

模块详细介绍

在上述方案中,实现单点登录的关键步骤包括:

  1. 用户中心提供接口验证原有 Token 并生成 Sa-Token:通过 /auth/check 接口,用户中心接收原有业务系统的 Token,验证其有效性并生成新的 Sa-Token。
  2. 网关服务拦截所有请求,验证和生成 Sa-Token:网关服务在接收到请求时,首先检查请求头中的原有 Token,并调用用户中心的 /auth/check 接口来验证 Token 并生成 Sa-Token,将生成的 Sa-Token 存储在 Redis 中,并添加到请求头中转发给后端服务。

这些步骤确保了用户在登录后,可以使用原有 Token 进行验证,并通过 Sa-Token 进行单点登录的验证,从而实现了单点登录的效果。

具体的实现过程

  1. 用户登录生成原有业务系统的 Token

    • 用户通过用户中心的登录接口进行登录。
    • 用户中心生成原有业务系统的 Token 并返回给前端。
  2. 网关服务处理用户请求

    • 前端在请求其他服务时,将原有的 Token 添加到请求头中,发送请求到网关。
    • 网关服务拦截请求,获取请求头中的原有 Token。
  3. 网关服务验证和生成 Sa-Token

    • 网关服务调用用户中心的 /auth/check 接口,验证原有 Token 的有效性。
    • 用户中心验证原有 Token 有效后,生成 Sa-Token 并返回给网关服务。
    • 网关服务将原有 Token 和 Sa-Token 的映射关系存储在 Redis 中。
    • 网关服务将生成的 Sa-Token 添加到请求头中,转发请求到后端服务。
  4. 后端服务处理请求

    • 后端服务可以继续保持现有的 Token 验证逻辑,不需要改动。
    • 所有的 Token 验证和生成逻辑都在网关和用户中心处理。

实现细节

1. 用户中心新增 Token 验证和生成 Sa-Token 接口
@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/check")
    public ResponseEntity<SaResult> checkToken(@RequestParam String token) {
        // 验证原有 Token
        if (validateOriginalToken(token)) {
            String userId = getUserIdFromOriginalToken(token);
            StpUtil.login(userId);
            String saToken = StpUtil.getTokenValue();
            // 存储原有 Token 和 Sa-Token 的映射关系
            redisTemplate.opsForValue().set("sa-token:" + token, saToken);
            redisTemplate.opsForValue().set("original-token:" + saToken, token);
            return ResponseEntity.ok(SaResult.ok("Token 有效").set("saToken", saToken));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(SaResult.error("Token 无效"));
    }

    private boolean validateOriginalToken(String token) {
        // 验证原有业务系统的 Token
        return true; // 假设校验成功
    }

    private String getUserIdFromOriginalToken(String token) {
        // 从原有业务系统的 Token 中解析出用户ID
        return "parsed-user-id";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
2. 网关服务 Token 校验过滤器
@Component
public class TokenFilter implements GlobalFilter, Ordered {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String originalToken = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (originalToken != null) {
            String saToken = redisTemplate.opsForValue().get("sa-token:" + originalToken);
            if (saToken == null) {
                // 调用用户中心的接口生成 Sa-Token
                saToken = generateSaToken(originalToken);
                if (saToken == null) {
                    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                    return exchange.getResponse().setComplete();
                }
            }
            exchange.getRequest().mutate().header("Sa-Token", saToken).build();
            return chain.filter(exchange);
        }
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

    private String generateSaToken(String originalToken) {
        // 调用用户中心的接口生成 Sa-Token
        String url = "http://user-center/auth/check?token=" + originalToken;
        ResponseEntity<SaResult> response = restTemplate.getForEntity(url, SaResult.class);
        if (response.getStatusCode() == HttpStatus.OK && response.getBody().getCode() == 200) {
            return response.getBody().get("saToken").toString();
        }
        return null;
    }

    @Override
    public int getOrder() {
        return -100;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

工作流程总结

  1. 用户登录并获取原有业务系统的 Token。
  2. 用户在请求服务时,携带原有 Token 发送请求到网关。
  3. 网关验证原有 Token,并通过用户中心生成 Sa-Token。
  4. 网关将 Sa-Token 存储在 Redis 中,并添加到请求头中转发给后端服务。
  5. 后端服务保持现有逻辑不变,网关和用户中心负责所有的 Token 验证和生成。

通过以上步骤,达成了在现有系统中实现的目标,同时最大限度地减少了对现有系统的改动。

五、总结

通过上述详细的改造步骤和代码示例,可以在不改动后端服务的情况下,实现单点登录。所有的 Token
验证和生成逻辑都集中在网关和用户中心,实现了 Token 的统一管理和验证。这样既实现了项目之间跳转的目标,又最大限度地减少了对现有系统的改造。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/1006346
推荐阅读
相关标签
  

闽ICP备14008679号