微服务场景实战:基于SpringCloud Alibaba从零搭建鉴权中心服务(3)

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: 微服务场景实战:基于SpringCloud Alibaba从零搭建鉴权中心服务(3)

Controller

我们需要给注册用户和生成token 一个程序的入口


就是我们的 AuthorityController,这里可以用到我们之前使用的注解@IgnoreResponseAdvice我们为啥那么不让他封装呢,我们需要验证,单纯的 JwtToken对象就可以了,不需要封装和转化


@Slf4j
@RestController
@RequestMapping("/authority")
public class AuthorityConroller {
    private final IJWTService ljwtService;
    public AuthorityConroller(IJWTService ljwtService) {
        this.ljwtService = ljwtService;
    }
    /*
     * 从授权中心 获取 token (其实就是登陆功能) 且返回信息中没有统一响应的包装
     * */
    @IgnoreResponseAdvice
    @PostMapping("/token")
    public JwtToken token(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception {
        //通常 日志里不会答打印用户的信息 防止泄露,我们这本身就是一个授权服务器,本身就不对外开放,所以我们可以打印用户信息到日志方便查看
        log.info("request to get token with param:[{}]", JSON.toJSONString(usernameAndPassword));
        return new JwtToken(ljwtService.generateToken(
                usernameAndPassword.getUsername(),
                usernameAndPassword.getPassword()));
    }
    /*注册用户并且返回注册当前用户的token 就是通过授权中心常见用户*/
    @IgnoreResponseAdvice
    @PostMapping("/register")
    public JwtToken register(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception {
        log.info("register user with param:[{}]", JSON.toJSONString(usernameAndPassword));
        return new JwtToken(ljwtService.registerUserAndGenerateToken(usernameAndPassword));
    }
}

鉴权编码实现

这里我们打鉴权 放到公共模块里 为什么呢,这里我们不止是鉴权中心还有其他的服务也要用到鉴权服务,秉着封装的思想,我们提取公共的方法放到 Common里面


创建JWT Token解析类TokenParseUtil


/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: JWT Token 解析工具类
 * @params :  null
 * @return :  * @return : null
 */
public class TokenParseUtil {
    public static LoginUserinfo parseUserInfoFromToken(String token) throws Exception {
        if (null == token) {
            return null;
        }
        Jws<Claims> claimsJws = parseToken(token, getPublicKey());
        Claims body = claimsJws.getBody();
        //如果 Token 已经过期返回 null
        if (body.getExpiration().before(Calendar.getInstance().getTime())) {
            return null;
        }
        //     返回 Token中保存的用户信息
        return JSON.parseObject(
                body.get(CommonCanstant.JWT_USER_INFO_KEY).toString(), LoginUserinfo.class
        );
    }
    /*
     * 通过公钥去解析 JWT Token
     * */
    private static Jws<Claims> parseToken(String token, PublicKey publicKey) {
        // 用设置签名公钥,解析claims信息 token
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }
    /*
     * 根据本地存储的公钥获取到 getPublicKey
     * */
    public static PublicKey getPublicKey() throws Exception {
        //解码器 我们设置解码器 将公钥放进去
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
                new BASE64Decoder().decodeBuffer(CommonCanstant.PUBLIC_KEY)
        );
        //创建 RSA 实例 通过示例生成公钥对象
        return KeyFactory.getInstance("RSA").generatePublic(keySpec);
    }
}

这里是涉及到一个问题 ,token要是传输的不是jwt token对象,会跑出异常,没有兜底,


其实这里这问题其实也不成立,应为你没有传入token对象,我们这里抛出异常是正确的,也不会影响其他服务,之后搭配sentinel和豪猪哥 可以实现异常重启等等,这里我们就先不编写兜底方法,以解析jwt token为主。


验证鉴权授权

我们写一个 test 类来测试 授权和鉴权拿到对象,是否有效

/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: JWT 相关测试类
 * @params :  null
 * @return :  * @return : null
 */
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class JWTServiceTest {
    @Autowired
    private IJWTService ijwtService;
    @Test
    public void testGenerateAndParseToken() throws Exception {
        String jwtToken = ijwtService.generateToken(
                "hyc@qq.com", "e10adc3949ba59abbe56e057f20f883e"
        );
        log.info("jwt token is:[{}]", jwtToken);
        LoginUserinfo userinfo = TokenParseUtil.parseUserInfoFromToken(jwtToken);
        log.info("userinfo by jwt prase token :[{}]", JSON.toJSONString(userinfo));
    }
}

启动测试查看结果

2.png

eyJhbGciOiJSUzI1NiJ9
.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIzNDgwNjdjMi00MTBlLTQ3MjItYmM3ZS02NWQyYmNmYTRkN2MiLCJleHAiOjE2Mzg3MjAwMDB9
.ZbFl81MkIipJSULZLf4F2X2Fb0q1TwhHIMT7nyZsZVwUxXyZnK54RlzoGM_b-kMUdKO_Tab-qEeOT6Jn--FiKmbOziWXiBx3a-k5ipthMJx0Fez-X8Acty-Pg7zukNalugiLxGb5ophQoVQWRTDmv2hytGHqiV71HVyErznkJa36QQr6QsjXqlJleo3BBt-6BFzdTFPLUmdTEJ4XsmZBa_acUDGBhY0_tU2gYtKBWhwvMCknuyCcV-_GVI5EvgMIKRpeFSZrWfTsDG2y1MFcyzjKE6jnzek-YwT3XkzQ8eGzUbiOlaU_Zx5OJah-UtrKwqlAw9WbO71pNgEBefdsYw

这是封装好的 JWT Token 这里我们可以看到三个点分别分割 了 header和payload以及签名,和我们之前讲的 结构一模一样,


userinfo by jwt prase token :[{"id":11,"username":"hyc@qq.com"}]

1

获取到的我们放在 jwt 里面需要传递的对象


验证对外提供的接口是否好用

这里我们编写 http脚本来测试对外题提供的接口是否有用


Token 方法


### 获取 Token -- 登录功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
Content-Type: application/json
{
  "username": "hyc@qq.com",
  "password": "e10adc3949ba59abbe56e057f20f883e"
}
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:35:52 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
  "token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIxNDU1M2FjZi1lZmE5LTQ4OTgtOTliYS1hNzA4NWI4MjU4MzAiLCJleHAiOjE2Mzg3MjAwMDB9.AlOpo6uf97R20ZLojXeun-3MK8DpSYlWxEygvDrtQeWaM9R0iKx-iW1VXnK6WoEntvqPxIrmPA7khjl3dXPa8kQHtdq-LVO7BDuZZDiQyZ64ZS7A9jWZr5JReSWBUSR1YUnsOvBRMkx4JVcAF3_W7nHwd722FFzOZRCr72hLHQIKpsugKtqjMEtaiEW0vcqphCYRJTAO_rQx1Lb1eVVg_Ufur0qSlKkV5dSJ0x3x9mc9UZRckwN0rrP7wQxZcrxJvKTfX7CkRRSO-CxZbG4WLokSaMtaGBMWU-7KGq7HSCZ0yuOgbbLdouHncsp6VD2tNLFdWSdJ_whCIbZxfX8R7w"
}

获取 token 成功


这里他没有被响应包裹,证明我们之前的选择屏蔽注解也生效了,很符合我们的预期


验证如果记录数据表没有是否会返回null


### 获取 Token -- 登录功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
Content-Type: application/json
### 随便写的id
{
"username": "hyc1111@qq.com",
"password": "e10adc3949ba59abbe56e057f20f883e"
}

返回结果 也符合我们预期 是 null


POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:40:44 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
  "token": null
}

register

### 注册用户并返回 Token -- 注册功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
Content-Type: application/json
{
  "username": "hyc@qq.com",
  "password": "e10adc3949ba59abbe56e057f20f883e"
}

这个用户之前是注册过的,我们来看一下是否会返回我们预期的处理

POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:42:00 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
  "token": null
}

现在我们去注册一个新的用户

### 注册用户并返回 Token -- 注册功能实现
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
Content-Type: application/json
{
  "username": "hyc11@qq.com",
  "password": "e10adc3949ba59abbe56e057f20f883e"
}


POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Dec 2021 15:42:57 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
  "token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEyLFwidXNlcm5hbWVcIjpcImh5YzExQHFxLmNvbVwifSIsImp0aSI6IjMxNDc0NmIwLTMyOGYtNDZkNS05ZTIwLTg3YjI0OWY1ZjZkOCIsImV4cCI6MTYzODcyMDAwMH0.MKxk-Q4BG5kaYFAsLiy13trtk_gDFmCKORpdE4EAwgSVecXFQcYfT1VvqSAKvoQLFsSlQAxOR5elV8CFOoKwAomwqdyyghZp63NKJ2smRbg3Y-4jWBzFVsUgcjOY2fwh7oNTdHEsWmLBYAh5r0hm_MysZsUEsE-cwb3sw8NSMk1OZp0J6tcRras7V1Uw5xXH8OnCoq2cUfdynJMHS29EzJT1TFPb8unVQ_A1RWodsHdK3n1Bl4wFbJjMtnHx7vzOeAUSNJx1XpAGdo0xYHK6HBpS9E1KBS3x1AnYFONM0DKd4-_QxMkBW1kkg2uWrRpf3GYZF20FKxXgmBAPHGZhew"
}

2.png

对象生成,功能验证一切正常


鉴权服务中心总结

对比基于Token与基于服务器的身份认证


传统:


最为传统的做法,客户端储存 cookie 一般是 Session id 服务器存储 Session

Session 是每次用户认证通过以后 ,服务器需要创建一条记录保存用户信息,通常是在内存中(也可以放在redis中),随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大

不同域名之前切换的时候,请求可能会被禁止,即跨越问题

2.png


基于token


JWT与Session的差异相同点是,他们都是存储用户信息。然而Session是在服务器端的,而JWT是在客户端的

JWT方式将用户状态分散到了客户端中,可以明显减轻请服务器的内存压力,服务端只需要用算法解析客户端的token就可以得到信息

3.png


两者优缺点的对比


解析方法:JWT使用算法直接解析得到用户信息;Session需要额外的数据映射。实现匹配

管理方法:JWT只有过期时间的限制,Session 数据保存在服务器,可控性更强

跨平台:JWT就是一段字符串,可以任意传播,Session跨平台需要有统一的解析平台,较为繁琐

时效性:JWT一旦生成 独立存在,很难做到特殊的控制;Session时效性完全由服务端的逻辑说了算

TIPS :各自都有优缺点,都是登陆和授权的解决方案


相关文章
|
30天前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
53 2
|
9天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
38 3
|
17天前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
55 4
|
1月前
|
JSON SpringCloudAlibaba Java
Springcloud Alibaba + jdk17+nacos 项目实践
本文基于 `Springcloud Alibaba + JDK17 + Nacos2.x` 介绍了一个微服务项目的搭建过程,包括项目依赖、配置文件、开发实践中的新特性(如文本块、NPE增强、模式匹配)以及常见的问题和解决方案。通过本文,读者可以了解如何高效地搭建和开发微服务项目,并解决一些常见的开发难题。项目代码已上传至 Gitee,欢迎交流学习。
128 1
Springcloud Alibaba + jdk17+nacos 项目实践
|
25天前
|
消息中间件 自然语言处理 Java
知识科普:Spring Cloud Alibaba基本介绍
知识科普:Spring Cloud Alibaba基本介绍
56 2
|
1月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
30天前
|
JSON Java 数据格式
【微服务】SpringCloud之Feign远程调用
本文介绍了使用Feign作为HTTP客户端替代RestTemplate进行远程调用的优势及具体使用方法。Feign通过声明式接口简化了HTTP请求的发送,提高了代码的可读性和维护性。文章详细描述了Feign的搭建步骤,包括引入依赖、添加注解、编写FeignClient接口和调用代码,并提供了自定义配置的示例,如修改日志级别等。
77 1
|
1月前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
1月前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
43 1
|
2月前
|
前端开发 API 微服务
SpringCloud微服务之间使用Feign调用不通情况举例
SpringCloud微服务之间使用Feign调用不通情况举例
505 2