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

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

编码


编写生成公钥密钥的测试类,创建 一些我们常用的VO对象 用来储存我们常用的一些变量,比如用户信息,公钥,密钥,一些常用的属性 放进 VO的模型里


@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
/**
 *
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: RSA 非对称 加密算法
 * @params :  null 
 * @return :  * @return : null
 */
public class RSATest {
    @Test
    public void generateKeyBytes() throws Exception {
        /*获取到 RSA算法实例*/
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        /* 这里最小是 2048 低于的话 是会报错的*/
        keyPairGenerator.initialize(2048);
        /*
         * 生成公钥对
         * */
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        /*获取 公钥和私钥对象*/
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        log.info("private key:[{}]", Base64.encode(privateKey.getEncoded()));
        log.info("public key:[{}]", Base64.encode(publicKey.getEncoded()));
    }
}

创建VO对象保存 我们常用且不会变化的值和对象


存储私钥 应为是私钥 所以只对鉴权中心 暴露 于是我们在鉴权服务中创建Constant包创建这个AuthotityConstant类保存信息


/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: 鉴权的常量
 * @params :  null
 * @return :  * @return : null
 */
public class AuthorCanstant {
    /*私钥 只暴露给 鉴权中心 不暴露给任何的其他服务*/
    public static final String PRIVATE_KEY = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBA" +
            "QCMXrQCudalKHJlH16YHr9mI5/xyYnkp5u2gAbMFf2xAHAyykYmixJP3CqG2a8tUwiJjjTIJXP+79Jzgjgg" +
            "VbBaTakrvjeFXz9HNP1D4XD6Li+sRVjnN1iBUwIFRxiFN2EOJflA9bqeQLAge/LgAu06y3jdLLleJF7yDRuMH" +
            "YedqPl9AJa5RdJmt0OgCoVOqacB7oGkFCFISm0Cwjfgq06nyiiULGZNVt8uhDxZAE4Pi2lmf3yggXCBH9AtU/2" +
            "XdyxU9caQJOAbYGxd/mART/NivBjSqo60wcBnktI+booUbDKRBbWRxvfYqKWEwPOwxlJUB3l3pcLZm866Xl3qtVM" +
            "XAgMBAAECggEADCGjLRkik+OK/3JWmo8Nu6YYjKz+XeSecIdgDwNXiZSgHcOdjHc4fe5pPn5RxXkHo9vGdAXIoJ/Z" +
            "cGIwt5qwQx2zITSvV7eDoIPT36n8OaMEO79Cj7kYzRR/eDVMyTagDLj7ccHK/yJYFnaf5vxZxFsRdwwGeTxreD" +
            "/pwZJLxjRSz1W57v5yUJNPPimNB229EogNYHIhQ8+Z7OGiilbtBIL9r6lqlz2hUAVBzXl4kOXFVI+vEodLuV2" +
            "rtQXXrpO1+AgH5lZJ7ahShKbqHt/Q6uJSTKAhbsfv/iadcPjmYp2F7nnYBLf66Jln6AWUwnXrJ7XETOf/+Qcib" +
            "q/5m6RjAQKBgQDruxn+kaDr5uYQMVSHog+CBRBJghJ4JklhY7ZDYJ2wN2KNHOd3mW/wUVDihVIyRFniIzsWU" +
            "0lnI+4OLqNLAZOBaQB5VrjyH4fxn5b26t0xLO1d5EWcOYI8ZRhwWDWaZipe2dUMeqVVMYFeDdTdNsyGrf8x" +
            "L+OVyRDiH4s4pBIs7QKBgQCYcIVFgDbrmwsP7lA9/dU9kClutY3gjEUgB2IJp2Y8S4Xhfi4NC8GqRQoMUyuqg" +
            "vPHKEiTCa1EojGHS/+r4JVcSg9Wsv64SpGZ+gANxRhfYFPrbkjU4YOMaZeCGUfKR2QnD20c3I4gdQ9kU5nK52n+Y" +
            "JEkAFUejg1Mhb6Fp6HDkwKBgAHYYBa3CxxtnUVpLXE2Woq5AWyh4QUhv5dMkYOrgPB9Ln9OR52PDOpDqK9tP" +
            "bx4/n8fqXm+QyfUhyuDP/H5XC86JC/O9vmmN4kzp5ndMsgMwvrmK4lShet1GyDd/+VqgVBmwh0r5JlrHske" +
            "sJjesfEn8YRwDIcCoOg0OQHDfwTtAoGAQfE61YvXNihFqsiOkaKCYjVAlxGWpDJJnMdU05REl4ScD6WDy" +
            "kTxq/RdmmNIGmS3i8mTS3f+Khh3kG2B1ho6wkePRxP7OEGZpqAM8ef22RtUch2tB9neDBmJXtAMzCYB3xu/O" +
            "aL3IHdDB0Va2/krUsz3PDmgmK0ed6HLfwm64l0CgYB+iGkMAQEwqYmcCEXKK825Q9y/u8PE9y8uaMGfsZQzDo6v" +
            "V5v+reOhmZRrk5BnX+pgztbE28sS6c2vYR0RYoR90aD2GXungCPXWEMDQudHFxvSsNTCYkDynjTSlnzu9aDcfqw1" +
            "UIzHog2zCquSro7tnbOMsvV5UdsLBq+WNQGgAw==";
    /*默认的 token 超时时间,一天*/
    public static final Integer DEFAULT_EXPIRE_DAY = 1;
}

之后是创建一些公共常用的VO模型 e-commerce-common


保存 公钥到公用包 以后我们的服务 需要做授权都需要使用到


/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: 通用模块的常量定义
 * @params :  null
 * @return :  * @return : null
 */
public class CommonCanstant {
    /* RSA 公钥*/
    public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjF60ArnWpShyZ" +
            "R9emB6/ZiOf8cmJ5KebtoAGzBX9sQBwMspGJosST9wqhtmvLVMIiY40yCVz/u/Sc4I4IFWwWk2pK743hV8/RzT9Q+F" +
            "w+i4vrEVY5zdYgVMCBUcYhTdhDiX5QPW6nkCwIHvy4ALtOst43Sy5XiRe8g0bjB2Hnaj5fQCWuUXSZrdDoAqFTqmnA" +
            "e6BpBQhSEptAsI34KtOp8oolCxmTVbfLoQ8WQBOD4tpZn98oIFwgR/QLVP9l3csVPXGkCTgG2BsXf5gEU/zYrwY0qqO" +
            "tMHAZ5LSPm6KFGwykQW1kcb32KilhMDzsMZSVAd5d6XC2ZvOul5d6rVTFwIDAQAB";
    /* JWT 中 存储用户信息到 key*/
    public static final String JWT_USER_INFO_KEY = "e-commerce-user";
    /*授权中心的 service-id*/
    public static final String AUTHORITY_CENTER_SERVICE_ID = "e-commerce-authity-center";
}

用户信息的常用VO对象


/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: 授权中心 鉴权 之后给客户端的token
 * @params :  null
 * @return :  * @return : null
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JwtToken {
    /* JWT*/
    private String token;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUserinfo {
    /*用户 id*/
    private Long id;
    /*用户名*/
    private String username;
}
/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context:用户名和密码
 * @params :  null
 * @return :  * @return : null
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UsernameAndPassword {
    /*用户名 */
    private String username;
    /*密码*/
    private String password;
}

授权服务编写


首先创建一个 接口 IJWTService


定义我们需要实现的授权方法


/**
 * @author : 冷环渊
 * @date : 2021/12/5
 * @context: JWT 相关服务接口定义
 * @params :  null
 * @return :  * @return : null
 */
public interface IJWTService {
    /*
     * 生成 token 使用默认的超时时间
     * */
    String generateToken(String username, String password) throws Exception;
    /*
     * 生成 JWT Token 可以设置超时时间 单位是天
     * */
    String generateToken(String username, String password, Integer expireTime) throws Exception;
    /*
     * 注册用户并且生成 token 返回
     * */
    String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception;
}

授权方法实现类


这里我们有三个方法实现


默认超时时间的 生成 token

自定义超时时间的设置生成token

注册新用户并且生成的token返回

JWT对象生成细节:


1) 我们需要设置需要传递的对象

2)我们需要设置一个不重复的 id

3)我们需要设置超时时间

4)设置我们的加密签名

5)完成设置返回字符串对象


Jwts.builder()
                //这里 claim 其实就是 jwt 的 payload 对象 --> KV
                .claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo))
                // jwt id 表示是 jwt的id
                .setId(UUID.randomUUID().toString())
                //jwt 的过期时间
                .setExpiration(expireDate)
                // 这里是设置加密的私钥和加密类型
                .signWith(getPrivateKey(), SignatureAlgorithm.RS256)
                //生成 jwt信息 返回的是一个字符串类型
                .compact();
    }

完整代码


@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class IJWTServiceIpml implements IJWTService {
    @Autowired
    private EcommerceUserDao ecommerceUserDao;
    @Override
    public String generateToken(String username, String password) throws Exception {
        return generateToken(username, password, 0);
    }
    @Override
    public String generateToken(String username, String password, Integer expireTime) throws Exception {
        //首先需要验证用户是否通过授权校验,即 输入的用户名和密码能否寻找到匹配数据表的记录
        EcommerceUser ecommerceUser = ecommerceUserDao.findByUsernameAndPassword(username, password);
        if (ecommerceUser == null) {
            log.error("can not find user:[{}],[{}]", username, password);
            return null;
        }
        //Token 中塞入对象, 即 JWT中 储存的对象,后端拿到这些信息 就可以知道那个用户在操作
        LoginUserinfo loginUserinfo = new LoginUserinfo(
                ecommerceUser.getId(), ecommerceUser.getUsername()
        );
        if (expireTime <= 0) {
            expireTime = AuthorCanstant.DEFAULT_EXPIRE_DAY;
        }
        //计算超时时间
        ZonedDateTime zdt = LocalDate.now().plus(expireTime, ChronoUnit.DAYS)
                .atStartOfDay(ZoneId.systemDefault());
        Date expireDate = Date.from(zdt.toInstant());
        return Jwts.builder()
                //这里 claim 其实就是 jwt 的 payload 对象 --> KV
                .claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo))
                // jwt id 表示是 jwt的id
                .setId(UUID.randomUUID().toString())
                //jwt 的过期时间
                .setExpiration(expireDate)
                // 这里是设置加密的私钥和加密类型
                .signWith(getPrivateKey(), SignatureAlgorithm.RS256)
                //生成 jwt信息 返回的是一个字符串类型
                .compact();
    }
    @Override
    public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception {
        //先去校验 用户名是否存在 如果存在 不能重复注册
        EcommerceUser oldUser = ecommerceUserDao.findByUsername(usernameAndPassword.getUsername());
        if (null != oldUser) {
            log.error("username is registered:[{}]", oldUser.getUsername());
            return null;
        }
        EcommerceUser ecommerceUser = new EcommerceUser();
        ecommerceUser.setUsername(usernameAndPassword.getUsername());
        ecommerceUser.setPassword(usernameAndPassword.getPassword()); //MD5 编码以后
        ecommerceUser.setExtraInfo("{}");
        //注册一个新用户 写到一个 记录表中
        ecommerceUser = ecommerceUserDao.save(ecommerceUser);
        log.info("regiter user success:[{}],[{}]", ecommerceUser.getUsername());
        //生成 token 并且返回
        return generateToken(ecommerceUser.getUsername(), ecommerceUser.getPassword());
    }
    /*
     * 根据本地储存的私钥获取到 PrivateKey对象
     * */
    private PrivateKey getPrivateKey() throws Exception {
        //使用给定的编码密钥创建一个新的PKCS8EncodedKeySpec。
        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(AuthorCanstant.PRIVATE_KEY));
        // 设置生成新密钥的工厂加密方式
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //返回生成好的密钥
        return keyFactory.generatePrivate(priPKCS8);
    }
}

之后我们的授权都会使用到以上的方法


相关文章
|
6月前
|
负载均衡 算法 Java
【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign
Feign是一个WebService客户端。使用Feign能让编写WebService客户端更加简单。 它的使用方法是定义一个服务接口然后再上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,十七支持了SpringMVC标准注解和HttpMessageConverters。 Feign可用于Eureka和Ribbon组合使用以支持负载均衡
866 139
|
6月前
|
算法 Java 微服务
【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
微服务架构是What?? 微服务架构是一种架构模式,它提出将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。 每个服务允许在其独立的进程中,服务于服务间采用轻量级的通信机制互相协作(通常是Http协议的RESTful API或RPC协议)。 每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据上下文,选择合适的语言、工具对其进行构建
633 126
|
6月前
|
负载均衡 算法 Java
【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
1. 什么是服务治理? SpringCloud封装了Netfix开发的Eureka模块来实现服务治理 在传统pc的远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册
409 0
|
6月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
7月前
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
449 1
|
7月前
|
jenkins Java 持续交付
使用 Jenkins 和 Spring Cloud 自动化微服务部署
随着单体应用逐渐被微服务架构取代,企业对快速发布、可扩展性和高可用性的需求日益增长。Jenkins 作为领先的持续集成与部署工具,结合 Spring Cloud 提供的云原生解决方案,能够有效简化微服务的开发、测试与部署流程。本文介绍了如何通过 Jenkins 实现微服务的自动化构建与部署,并结合 Spring Cloud 的配置管理、服务发现等功能,打造高效、稳定的微服务交付流程。
879 0
使用 Jenkins 和 Spring Cloud 自动化微服务部署
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
人工智能 SpringCloudAlibaba 自然语言处理
SpringCloud Alibaba AI整合DeepSeek落地AI项目实战
在现代软件开发领域,微服务架构因其灵活性、可扩展性和模块化特性而受到广泛欢迎。微服务架构通过将大型应用程序拆分为多个小型、独立的服务,每个服务运行在其独立的进程中,服务与服务间通过轻量级通信机制(通常是HTTP API)进行通信。这种架构模式有助于提升系统的可维护性、可扩展性和开发效率。
5045 2
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
3052 13
Spring Cloud Alibaba:一站式微服务解决方案