Spring Security OAuth 笔记

虚幻大学 xuhss 331℃ 0评论

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
1 单点登录

关于单点登录的原理,我觉得下面这位老哥讲的比较清楚,有兴趣可以看一下,下面我把其中的重点在此做个笔记总结

https://juejin.cn/post/6844904079274197005

主流的单点登录都是基于共享 cookie 来实现的

1.1 同域单点登录

适用场景:都是企业内部系统,所有系统都适用同一个一级域名,并通过不同的二级域名区分

举个例子:公司有一个一级域名cjs.com,我们有三个系统需要实现单点登录,分别是门户系统(sso.cjs.com)、应用系统1(app1.cjs.com)、应用系统2(app2.cjs.com)

a69b41297fe6007734decd9224a0c72d - Spring Security OAuth 笔记

核心原理

  1. 门户系统设置 Cookie 的 domain 为一级域名也就是 cjs.com,这样就可以共享门户的 Cookie 给所有的使用该域名(xxx.cjs.com)的系统
  2. 使用 Spring Session 等技术让所有系统共享 Session
  3. 所有登录都跳转到门户系统去登录,也就说门户系统有两个页面就够了:登录页(login.html)和首页(index.html)。通过首页链接可以进入到各子业务系统。
  4. 可以在加一层网关(Spring Cloud Gateway)

1.2 跨域单点登录

由于域名不一样不能共享 Cookie 了,这样就需要通过一个单独的授权服务(UAA)来做统一登录,并基于共享UAA的 Cookie 来实现单点登录。

举个例子:公司接到一个大项目,把其中部分系统外包给第三方来做,或者直接采购第三方服务商的系统,或者是子业务系统1采购服务商A的系统,子系统2采购B服务商的系统。无论什么情况,总之系统集成就需要单点登录。

ea6c19076fc005bc86f09967c9d4a27d - Spring Security OAuth 笔记

核心原理

  1. 用户访问系统1,如果未登录,则跳转到UAA系统请求授权,并输入用户名/密码完成登录
  2. 登录成功后UAA系统把登录信息保存到 Session 中,并在浏览器写入域为 sso.com 的 Cookie
  3. 用户访问系统2,如未登录,则跳转到UAA系统请求授权
  4. 由于是跳转到UAA系统的域名 sso.com 下,所以能通过浏览器中UAA的 Cookie 读取到 Session 中之前的登录信息完成单点登录

1.3 基于OAuth2的跨域单点登录

0c6f686417bc83e1bb5e12e00ca3b198 - Spring Security OAuth 笔记

1.4 前后端分离的跨域单点登录

前后端分离的核心概念是后端仅返回前端所需的数据,不再渲染HTML页面,前端HTML页面通过AJAX调用后端的RESTFUL API接口并使用JSON数据进行交互

跨域间的前后端分离项目也是基于共享统一授权服务(UAA)的cookie来实现单点登录的,但是与非前后分离不一样的是存在以下问题需要解决

  1. 没有过滤器/拦截器,需要在前端判断登录状态
  2. 需要自己实现oauth2的授权码模式交互逻辑
  3. 需要解决安全性问题,oauth2的clientSecret参数放在前端不安全

bc0869b7499e66db9e9069490a8a7880 - Spring Security OAuth 笔记

补充:前端获取授权码

  • redirect_uri写前端地址
  • 重定向到前端页面,页面获取到授权码code,拿code换token

示例参考:

http://localhost:9000/callback.html?code=xxx


 lang="zh">

  charset="UTF-8">
 Title
 <span style="color: rgba(166, 226, 46, 1)">type=</span><span style="color: rgba(230, 219, 116, 1)">"text/javascript"</span> <span style="color: rgba(166, 226, 46, 1)">src=</span><span style="color: rgba(230, 219, 116, 1)">"jquery-3.6.0.min.js"</span><span style="color: rgba(249, 38, 114, 1)">>
 </span>
 <span style="color: rgba(117, 113, 94, 1)">/**</span>
<span style="color: rgba(117, 113, 94, 1)"> * 获取指定请求参数的值</span>
<span style="color: rgba(117, 113, 94, 1)"> * @param name 请求参数名称</span>
<span style="color: rgba(117, 113, 94, 1)"> * @returns {string|null}</span>
<span style="color: rgba(117, 113, 94, 1)"> */</span>
 <span style="color: rgba(102, 217, 239, 1)">function</span> <span style="color: rgba(166, 226, 46, 1)">getQueryParameter</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">name</span><span style="color: rgba(248, 248, 242, 1)">)</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">queryString</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(248, 248, 242, 1)">window.</span><span style="color: rgba(166, 226, 46, 1)">location</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">search</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">substring</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(174, 129, 255, 1)">1</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">params</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(166, 226, 46, 1)">queryString</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">split</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(230, 219, 116, 1)">"&"</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(102, 217, 239, 1)">for</span> <span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">i</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(174, 129, 255, 1)">0</span><span style="color: rgba(248, 248, 242, 1)">;</span> <span style="color: rgba(166, 226, 46, 1)">i</span> <span style="color: rgba(249, 38, 114, 1)"><</span> <span style="color: rgba(166, 226, 46, 1)">params</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">length</span><span style="color: rgba(248, 248, 242, 1)">;</span> <span style="color: rgba(166, 226, 46, 1)">i</span><span style="color: rgba(249, 38, 114, 1)">++</span><span style="color: rgba(248, 248, 242, 1)">)</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">pair</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(166, 226, 46, 1)">params</span><span style="color: rgba(248, 248, 242, 1)">[</span><span style="color: rgba(166, 226, 46, 1)">i</span><span style="color: rgba(248, 248, 242, 1)">].</span><span style="color: rgba(166, 226, 46, 1)">split</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(230, 219, 116, 1)">"="</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(102, 217, 239, 1)">if</span> <span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">name</span> <span style="color: rgba(249, 38, 114, 1)">==</span> <span style="color: rgba(166, 226, 46, 1)">pair</span><span style="color: rgba(248, 248, 242, 1)">[</span><span style="color: rgba(174, 129, 255, 1)">0</span><span style="color: rgba(248, 248, 242, 1)">])</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(102, 217, 239, 1)">return</span> <span style="color: rgba(166, 226, 46, 1)">pair</span><span style="color: rgba(248, 248, 242, 1)">[</span><span style="color: rgba(174, 129, 255, 1)">1</span><span style="color: rgba(248, 248, 242, 1)">];</span>
 <span style="color: rgba(248, 248, 242, 1)">}</span>
 <span style="color: rgba(248, 248, 242, 1)">}</span>
 <span style="color: rgba(102, 217, 239, 1)">return</span> <span style="color: rgba(102, 217, 239, 1)">null</span><span style="color: rgba(248, 248, 242, 1)">;</span>
 <span style="color: rgba(248, 248, 242, 1)">}</span>
 <span style="color: rgba(117, 113, 94, 1)">/**</span>
<span style="color: rgba(117, 113, 94, 1)"> * 获取指定请求参数的值</span>
<span style="color: rgba(117, 113, 94, 1)"> * @param name 请求参数名称</span>
<span style="color: rgba(117, 113, 94, 1)"> * @returns {string|null}</span>
<span style="color: rgba(117, 113, 94, 1)"> */</span>
 <span style="color: rgba(102, 217, 239, 1)">function</span> <span style="color: rgba(166, 226, 46, 1)">getUrlParameter</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">name</span><span style="color: rgba(248, 248, 242, 1)">)</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">reg</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(102, 217, 239, 1)">new</span> <span style="color: rgba(248, 248, 242, 1)">RegExp(</span><span style="color: rgba(230, 219, 116, 1)">"(^|&)"</span> <span style="color: rgba(249, 38, 114, 1)">+</span> <span style="color: rgba(166, 226, 46, 1)">name</span> <span style="color: rgba(249, 38, 114, 1)">+</span> <span style="color: rgba(230, 219, 116, 1)">"=([^&]*)(&|$)"</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">queryString</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(248, 248, 242, 1)">window.</span><span style="color: rgba(166, 226, 46, 1)">location</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">search</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">substring</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(174, 129, 255, 1)">1</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">result</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(166, 226, 46, 1)">queryString</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">match</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">reg</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(102, 217, 239, 1)">if</span> <span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(102, 217, 239, 1)">null</span> <span style="color: rgba(249, 38, 114, 1)">!=</span> <span style="color: rgba(166, 226, 46, 1)">result</span><span style="color: rgba(248, 248, 242, 1)">)</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(102, 217, 239, 1)">return</span> <span style="color: rgba(166, 226, 46, 1)">unescape</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">result</span><span style="color: rgba(248, 248, 242, 1)">[</span><span style="color: rgba(174, 129, 255, 1)">2</span><span style="color: rgba(248, 248, 242, 1)">])</span>
 <span style="color: rgba(248, 248, 242, 1)">}</span>
 <span style="color: rgba(102, 217, 239, 1)">return</span> <span style="color: rgba(102, 217, 239, 1)">null</span><span style="color: rgba(248, 248, 242, 1)">;</span>
 <span style="color: rgba(248, 248, 242, 1)">}</span>
 <span style="color: rgba(102, 217, 239, 1)">let</span> <span style="color: rgba(166, 226, 46, 1)">authorizationCode</span> <span style="color: rgba(249, 38, 114, 1)">=</span> <span style="color: rgba(166, 226, 46, 1)">getUrlParameter</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(230, 219, 116, 1)">"code"</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(166, 226, 46, 1)">$</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">post</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(230, 219, 116, 1)">"http://localhost:8081/auth/oauth/token"</span><span style="color: rgba(248, 248, 242, 1)">,</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(166, 226, 46, 1)">grant\_type</span><span style="color: rgba(249, 38, 114, 1)">:</span> <span style="color: rgba(230, 219, 116, 1)">"authorization\_code"</span><span style="color: rgba(248, 248, 242, 1)">,</span>
 <span style="color: rgba(166, 226, 46, 1)">code</span><span style="color: rgba(249, 38, 114, 1)">:</span> <span style="color: rgba(166, 226, 46, 1)">authorizationCode</span><span style="color: rgba(248, 248, 242, 1)">,</span>
 <span style="color: rgba(166, 226, 46, 1)">redirect\_uri</span><span style="color: rgba(249, 38, 114, 1)">:</span> <span style="color: rgba(230, 219, 116, 1)">"http://localhost:9000/callback.html"</span>
 <span style="color: rgba(248, 248, 242, 1)">},</span> <span style="color: rgba(102, 217, 239, 1)">function</span> <span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">resp</span><span style="color: rgba(248, 248, 242, 1)">)</span> <span style="color: rgba(248, 248, 242, 1)">{</span>
 <span style="color: rgba(166, 226, 46, 1)">console</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">log</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(166, 226, 46, 1)">resp</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(166, 226, 46, 1)">sessionStorage</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">setItem</span><span style="color: rgba(248, 248, 242, 1)">(</span><span style="color: rgba(230, 219, 116, 1)">"token"</span><span style="color: rgba(248, 248, 242, 1)">,</span> <span style="color: rgba(166, 226, 46, 1)">resp</span><span style="color: rgba(248, 248, 242, 1)">.</span><span style="color: rgba(166, 226, 46, 1)">access\_token</span><span style="color: rgba(248, 248, 242, 1)">);</span>
 <span style="color: rgba(248, 248, 242, 1)">});</span>
 <span style="color: rgba(249, 38, 114, 1)">

2 Spring Security OAuth 2.0迁移指南

从 Spring Security 5.2.x 开始,OAuth 2.0 Clients 和 Resource Servers 已经从 Security OAuth 2.x 迁移到 从 Spring Security,而且 Spring Security 不再提供 Authorization Server 的支持。

397dc8412a368b2db3ec7668ebc69052 - Spring Security OAuth 笔记

总之呢,Spring Security OAuth这个项目以后就处于维护状态了,不会再更新了,建议使用Spring Security

迁移以后,很多地方都不一样了,就我注意到的说下几点变化

首先,以前单点登录使用@EnableOAuth2Sso注解,现在推荐使用oauth2Login()方法

44b8e58eb17c2e23eae0d618a8836f1a - Spring Security OAuth 笔记

其次,授权服务器的写法不一样了

d4b0eeb269f14f7a3c403c3cf341456f - Spring Security OAuth 笔记

默认的端点都变成 /oauth2 开头了

e14e943b99e18dbc2666d5a47c64201f - Spring Security OAuth 笔记

22fa20e27dcb0b20e22fd86765dd37ac - Spring Security OAuth 笔记

更多变化可以阅读源码,亦可参见 OAuth 2.0 Features Matrix 查看二者支持的特性

3 @EnableOAuth2Sso的作用

b04274f0fbb4ebd0bb752a3e861dbdc5 - Spring Security OAuth 笔记

@EnableOAuth2Sso: 标记服务作为一个OAuth 2.0客户端。这意味着它将负责将资源所有者(最终用户)重定向到授权服务器,在那里用户必须输入他们的凭据。完成后,用户被重定向回客户端,并携带授权码。然后,客户端获取授权码,并通过调用授权服务器以获取访问令牌。只有在此之后,客户端才能使用访问令牌调用资源服务器。

4 补充:根据pid递归查找子机构


package com.soa.supervision.gateway.service.impl;

import com.alibaba.fastjson.JSON;
import com.soa.supervision.gateway.entity.SysDept;
import com.soa.supervision.gateway.repository.SysDeptRepository;
import com.soa.supervision.gateway.service.SysDeptService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 机构表 服务实现类
 *
 * @author ChengJianSheng
 * @since 2022-03-08
 */
@Service
public class SysDeptServiceImpl implements SysDeptService {
    private static final String CACHE\_PREFIX = "DEPT:";
    @Resource
    private SysDeptRepository sysDeptRepository;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
 * 递归 向下查找当前机构的所有子机构
 */
    @Override
    public List getAllByPid(Integer pid, List list) {
        List subDeptIdList = sysDeptRepository.findIdByPid(pid);
        if (CollectionUtils.isEmpty(subDeptIdList)) {
            return new ArrayList<>();
        } else {
            list.addAll(subDeptIdList);
            subDeptIdList.forEach(e->{
                getAllByPid(e, list);
            });
        }
        return list;
    }

    @Override
    public String getSubDeptIdListByPid(Integer pid) {
        String key = CACHE_PREFIX + pid;
        String val = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(val)) {
            synchronized (SysDept.class) {
                if (StringUtils.isBlank(val)) {
                    List deptIds = getAllByPid(pid, new ArrayList<>());
                    deptIds.add(pid);
                    val = JSON.toJSONString(deptIds);
                    stringRedisTemplate.opsForValue().set(key, val, 1, TimeUnit.HOURS);
                }
            }
        }
        return val;
    }
}

package com.soa.supervision.gateway.repository;

import com.soa.supervision.gateway.entity.SysDept;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

/**
 * @Author ChengJianSheng
 * @Date 2022/3/8
 */
public interface SysDeptRepository extends JpaRepository {

    @Query(value = "SELECT id FROM sys\_dept WHERE pid = :pid", nativeQuery = true)
    List findIdByPid(@Param("pid") Integer pid);
}

5 有用的文档

Spring Security相关

Spring Boot OAuth相关

感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如需远程调代码或讲解或需要源码,请打赏!欢迎各位转载,但必须在文章页面中给出作者和原文链接!

转载请注明:xuhss » Spring Security OAuth 笔记

喜欢 (0)

您必须 登录 才能发表评论!