• 从零搭建微服务-认证中心(二)


    写在最前

    如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。

    源码地址(后端):https://gitee.com/csps/mingyue-springcloud-learning

    源码地址(前端):https://gitee.com/csps/mingyue-springcloud-ui

    文档地址:https://gitee.com/csps/mingyue-springcloud-learning/wikis

    创建新项目 MingYue

    Idea 创建 maven 项目这儿就不多赘述了,相信各位大佬都是信手拈来~

    MingYue Pom

    父项目的依赖都放在这儿了,后续用到什么再增加什么

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
    
        <groupId>com.csp.mingyuegroupId>
        <artifactId>mingyueartifactId>
        <version>1.0.0version>
        <packaging>pompackaging>
    
        <name>MingYuename>
        <description>MingYue 微服务系统description>
    
        <properties>
            <mingyue.version>1.0.0mingyue.version>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
            <maven.compiler.source>1.8maven.compiler.source>
            <maven.compiler.target>1.8maven.compiler.target>
            <java.version>1.8java.version>
    
            
            <spring-boot.version>2.7.11spring-boot.version>
            <spring-cloud.version>2021.0.6spring-cloud.version>
            <spring-cloud-alibaba.version>2021.0.5.0spring-cloud-alibaba.version>
    
            
            <jasypt.version>3.0.5jasypt.version>
            <hutool.version>5.8.18hutool.version>
            <lombok.version>1.18.26lombok.version>
            <spring.checkstyle.plugin>0.0.38spring.checkstyle.plugin>
        properties>
    
        
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-configuration-processorartifactId>
                <optional>trueoptional>
            dependency>
            
            <dependency>
                <groupId>com.github.ulisesbocchiogroupId>
                <artifactId>jasypt-spring-boot-starterartifactId>
                <version>${jasypt.version}version>
            dependency>
            
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <scope>providedscope>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    
        
        <dependencyManagement>
            <dependencies>
                
                <dependency>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-dependenciesartifactId>
                    <version>${spring-boot.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
                
                <dependency>
                    <groupId>org.springframework.cloudgroupId>
                    <artifactId>spring-cloud-dependenciesartifactId>
                    <version>${spring-cloud.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
                
                <dependency>
                    <groupId>com.alibaba.cloudgroupId>
                    <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                    <version>${spring-cloud-alibaba.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
    
                
                <dependency>
                    <groupId>cn.hutoolgroupId>
                    <artifactId>hutool-bomartifactId>
                    <version>${hutool.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
        dependencyManagement>
    project>
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    创建子项目 MingYue-Auth

    Idea mingyue 右击 -> New -> Module

    image-20230526103113168

    核心依赖

    <properties>
            <sa-token.version>1.34.0sa-token.version>
    properties>
    
    <dependencies>
      
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
      dependency>
    
      
      <dependency>
        <groupId>cn.dev33groupId>
        <artifactId>sa-token-spring-boot-starterartifactId>
        <version>${sa-token.version}version>
      dependency>
    
      
      <dependency>
        <groupId>cn.dev33groupId>
        <artifactId>sa-token-oauth2artifactId>
        <version>${sa-token.version}version>
      dependency>
    
      
      <dependency>
        <groupId>cn.dev33groupId>
        <artifactId>sa-token-dao-redis-jacksonartifactId>
        <version>${sa-token.version}version>
      dependency>
    
      
      <dependency>
        <groupId>org.apache.commonsgroupId>
        <artifactId>commons-pool2artifactId>
      dependency>
    
      
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
      dependency>
    dependencies>
    
    • 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

    SaOAuth2ServerController

    /**
     * Sa-OAuth2 Server端 控制器
     * @author kong
     *
     */
    @RestController
    public class SaOAuth2ServerController {
    
    	// 处理所有OAuth相关请求
    	@RequestMapping("/oauth2/*")
    	public Object request() {
    		System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
    		return SaOAuth2Handle.serverRequest();
    	}
    
    	// Sa-OAuth2 定制化配置
    	@Autowired
    	public void setSaOAuth2Config(SaOAuth2Config cfg) {
    		cfg.
    			// 未登录的视图
    			setNotLoginView(()->{
    				return new ModelAndView("login.html");
    			}).
    			// 登录处理函数
    			setDoLoginHandle((name, pwd) -> {
    				if("sa".equals(name) && "123456".equals(pwd)) {
    					StpUtil.login(10001);
    					return SaResult.ok();
    				}
    				return SaResult.error("账号名或密码错误");
    			}).
    			// 授权确认视图
    			setConfirmView((clientId, scope)->{
    				Map<String, Object> map = new HashMap<>();
    				map.put("clientId", clientId);
    				map.put("scope", scope);
    				return new ModelAndView("confirm.html", map);
    			})
    			;
    	}
    
    	// 全局异常拦截
    	@ExceptionHandler
    	public SaResult handlerException(Exception e) {
    		e.printStackTrace();
    		return SaResult.error(e.getMessage());
    	}
    
    
    	// ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------
    
    	// 获取Userinfo信息:昵称、头像、性别等等
    	@RequestMapping("/oauth2/userinfo")
    	public SaResult userinfo() {
    		// 获取 Access-Token 对应的账号id
    		String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
    		Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
    		System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
    
    		// 校验 Access-Token 是否具有权限: userinfo
    		SaOAuth2Util.checkScope(accessToken, "userinfo");
    
    		// 模拟账号信息 (真实环境需要查询数据库获取信息)
    		Map<String, Object> map = new LinkedHashMap<String, Object>();
    		map.put("nickname", "shengzhang_");
    		map.put("avatar", "http://xxx.com/1.jpg");
    		map.put("age", "18");
    		map.put("sex", "男");
    		map.put("address", "山东省 青岛市 城阳区");
    		return SaResult.data(map);
    	}
    
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    SaOAuth2TemplateImpl

    /**
     * Sa-Token OAuth2.0 整合实现
     * @author kong
     */
    @Component
    public class SaOAuth2TemplateImpl extends SaOAuth2Template {
    
    	// 根据 id 获取 Client 信息
    	@Override
    	public SaClientModel getClientModel(String clientId) {
    		// 此为模拟数据,真实环境需要从数据库查询
    		if("1001".equals(clientId)) {
    			return new SaClientModel()
    					.setClientId("1001")
    					.setClientSecret("aaaa-bbbb-cccc-dddd-eeee")
    					.setAllowUrl("*")
    					.setContractScope("userinfo")
    					.setIsAutoMode(true);
    		}
    		return null;
    	}
    
    	// 根据ClientId 和 LoginId 获取openid
    	@Override
    	public String getOpenid(String clientId, Object loginId) {
    		// 此为模拟数据,真实环境需要从数据库查询
    		return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
    	}
    }
    
    • 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

    添加 templates

    • confirm.html
    • login.html

    创建启动类启动项目

    @SpringBootApplication
    public class MingYueAuthApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(MingYueAuthApplication.class, args);
    		System.out.println("\nSa-Token-OAuth  Server端启动成功: http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo");
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    启动项目,访问打印地址:

    Sa-Token-OAuth Server端启动成功: http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo

    API 列表

    共四种模式,分别是授权码(Authorization Code)隐藏式(Implicit)密码式(Password)凭证式(Client Credentials),本文介绍了两种常用的:授权码(Authorization Code)、密码式(Password)

    1. 授权码(Authorization Code)

    http://localhost:9000/oauth2/authorize
    ?response_type=code
    &client_id=1001
    &redirect_uri=https://sa-token.cc&scope=userinfo
    
    • 1
    • 2
    • 3
    • 4

    参数详解:

    参数是否必填说明
    response_type返回类型,这里请填写:code
    client_id应用id
    redirect_uri用户确认授权后,重定向的url地址
    scope具体请求的权限,多个用逗号隔开
    state随机值,此参数会在重定向时追加到url末尾,不填不追加
    1.1 获取授权码

    Code授权码具有以下特点:

    1. 每次授权产生的 Code 码都不一样
    2. Code码用完即废,不能二次使用
    3. 一个Code的有效期默认为五分钟,超时自动作废
    4. 每次授权产生新 Code 码,会导致旧 Code 码立即作废,即使旧 Code 码尚未使用

    http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo

    打开地址,登录并同意授权

    image-20230525195925531

    地址栏拿到 code https://sa-token.cc/?code=ct0RB10UkGSwXINdBkGRGVKBvoKjIi6jzeYi3NFrCwoXzDr36Ljgm1ZBplYx

    1.2 根据授权码获取 Access-Token

    浏览器直接访问:

    http://localhost:9000/oauth2/token
        ?grant_type=authorization_code
        &client_id=1001
        &client_secret=aaaa-bbbb-cccc-dddd-eeee
        &code=ktN6FvIfB7pKqp7Thvkm32EfUhbveoybwOtmvqCCbuLdxevmyr9FW09Kd6qL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    参数是否必填说明
    grant_type授权类型,这里请填写:authorization_code
    client_id应用id
    client_secret应用秘钥
    code步骤1.1中获取到的授权码

    打印如下:

    {
        "code": 200,
        "msg": "ok",
        "data": {
            "access_token": "E1ZLgNXLiUIz8DmAxmgPeegMPsUCEJJLjUV9uwDQWCu4f6Tgg8U5JqKSrqSx",
            "refresh_token": "0IAWolcbMvoIpF8F1JcEuytfcoTe8nNVwsEdUHOy7rBX0V99vhPBvtFGfh62",
            "expires_in": 7199,
            "refresh_expires_in": 2591999,
            "client_id": "1001",
            "scope": "userinfo",
            "openid": "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2. 密码式(Password)

    首先在 Client 端构建表单,让用户输入 Server 端的账号和密码,然后在 Client 端访问接口

    http://localhost:9000/oauth2/token
        ?grant_type=password
        &client_id=1001
        &client_secret=aaaa-bbbb-cccc-dddd-eeee
        &username=sa
        &password=123456
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    参数是否必填说明
    grant_type返回类型,这里请填写:password
    client_id应用id
    client_secret应用秘钥
    username用户的Server端账号
    password用户的Server端密码
    scope具体请求的权限,多个用逗号隔开

    浏览器直接访问,打印如下:

    {
        "code": 200,
        "msg": "ok",
        "data": {
            "access_token": "BvU1yja6sy4WT3orCvIiYExIOhGMBwVps38ZywZWGaeYaM6nJbPkJRbHpmSC",
            "refresh_token": "TzSGmGhtPuAZUZ5ndfVETdmmpGr3ZwAH5N5kbrEYeeR2LKbLBXEysqsLxuV6",
            "expires_in": 7199,
            "refresh_expires_in": 2591999,
            "client_id": "1001",
            "scope": "",
            "openid": "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    至此,一个基于 OAuth2 的认证中心 Demo 就完成啦,接下来我们先优化一下代码结构。

  • 相关阅读:
    Vue - 插值 - 指令 - 过滤器 - 计算属性与监听属性 - 购物车案例
    从头训练RNN语言模型,这样的loss正常吗?
    vue状态管理——Vuex
    深度学习基础知识 使用torchsummary、netron、tensorboardX查看模参数结构
    AJAX之跨域问题
    应届生找嵌入式工作难吗?
    bgp路由更新属性
    冲刺十五届蓝桥杯P0002 日期统计
    Hadoop超详细入门(一)介绍及安装
    【创建型】原型模式(Prototype)
  • 原文地址:https://blog.csdn.net/csp732171109/article/details/130881215