微服务场景实战:基于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);
    }
}

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


相关文章
|
11月前
|
负载均衡 算法 Java
【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign
Feign是一个WebService客户端。使用Feign能让编写WebService客户端更加简单。 它的使用方法是定义一个服务接口然后再上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,十七支持了SpringMVC标准注解和HttpMessageConverters。 Feign可用于Eureka和Ribbon组合使用以支持负载均衡
1062 139
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1573 3
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
649 102
|
11月前
|
Cloud Native Serverless API
微服务架构实战指南:从单体应用到云原生的蜕变之路
🌟蒋星熠Jaxonic,代码为舟的星际旅人。深耕微服务架构,擅以DDD拆分服务、构建高可用通信与治理体系。分享从单体到云原生的实战经验,探索技术演进的无限可能。
微服务架构实战指南:从单体应用到云原生的蜕变之路
|
11月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1510 2
Spring Boot 3.x 微服务架构实战指南
|
负载均衡 监控 Java
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
在微服务架构中,高可用与稳定性至关重要。本文详解熔断、限流与负载均衡三大关键技术,结合API网关与Hystrix-Go实战,帮助构建健壮、弹性的微服务系统。
1086 1
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
|
NoSQL Java 微服务
2025 年最新 Java 面试从基础到微服务实战指南全解析
《Java面试实战指南:高并发与微服务架构解析》 本文针对Java开发者提供2025版面试技术要点,涵盖高并发电商系统设计、微服务架构实现及性能优化方案。核心内容包括:1)基于Spring Cloud和云原生技术的系统架构设计;2)JWT认证、Seata分布式事务等核心模块代码实现;3)数据库查询优化与高并发处理方案,响应时间从500ms优化至80ms;4)微服务调用可靠性保障方案。文章通过实战案例展现Java最新技术栈(Java 17/Spring Boot 3.2)的应用.
1141 9
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
3541 13
Spring Cloud Alibaba:一站式微服务解决方案