认证和SSO(一)-基于OAuth2单点登陆基本架构

1、前后端分离架构

  1.1、前后端半分离

  工作流程大致是,访问html页面,加载css、js等静态资源,加载到浏览器时,js会发送一些ajax请求由nginx转发到后端服务器,后端服务器给出相应的json数据,页面解析Json数据,通过Dom操作渲染页面。

  在这样的架构下,存在一些明显的弊端:

  SEO( 搜索引擎优化)非常不方便,由于搜索引擎的爬虫无法爬下JS异步渲染的数据,导致这样的页面,SEO会存在一定的问题;

  在Json返回的数据量比较大的情况下,渲染的十分缓慢,会出现页面卡顿的情况;

1.2、前后端分离

  将nginx替换成了NodeJS,浏览器请求NodeJS,NodeJS在发http请求去服务器获取json数据,NodeJS收到json后再渲染出HTML页面,NodeJS直接将HTML页面flush到浏览器,这样,浏览器得到的就是普通的HTML页面,而不用再发Ajax去请求服务器了。

在这里,前端不仅包括浏览器,还包括一个NodeJS服务器,后端只负责数据的处理。

2、前后端分离的OAuth2认证架构

  在之前我们实现的OAuth2认证架构中,客户端应用,其实是使用的一些http请求工具,如Restlet Client、Postman等。我们将架构改造为如下,客户端应用,其实应该由浏览器和前端服务器组成。(在这里前端服务器,我们使用SpringBoot模拟实现,在实际工作中,应该是由前端人员使用NodeJS来实现)

3、项目改造实现认证流程

  由上图可知,我们需要改造的有以下两点:第一步请求令牌时,将http请求工具更换为前端服务器来请求令牌;第五步由前端服务器携带令牌请求服务。

  3.1、搭建前端服务器(由SpringBoot模拟实现)

  3.1.1、项目结构

  3.1.2、页面index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>cfq security</title>
</head>

<body>
<h1>Study Security</h1>

<div id="login-success">
    <h1>登陆成功!</h1>

    <p>
        <button onclick="getOrder()">获取订单信息</button>
    </p>

    <table>
        <tr>
            <td>order id</td>
            <td><input id="orderId"/></td>
        </tr>
        <tr>
            <td>order product id</td>
            <td><input id="productId"/></td>
        </tr>
    </table>

    </hr>
    <p>
        <button onclick="logout()">退出</button>
    </p>

</div>

<div id="goto-login">
    <table>
        <tr>
            <td>用户名</td>
            <td><input id="username" name="username" value=""/></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input id="password" name="password" value=""/></td>
        </tr>
        <tr>
            <td colspan="2" align="right">
                <button id="submit" onclick="login()">登录</button>
            </td>
        </tr>
    </table>
</div>

<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
    //判断用户是否登陆
    $(function () {
        $.get("/me", function (data) {
            if (data) {
                //已登录
                $("#login-success").show();
                $("#goto-login").hide();
            } else {
                //未登录
                $("#login-success").hide();
                $("#goto-login").show();
            }
        });
    });

    //登陆方法
    function login() {
        var username = $("#username").val();
        var password = $("#password").val();

        $.ajax({
            type: "POST",
            url: "/login",
            contentType: "application/json;charset=utf-8",
            data: JSON.stringify({"username": username, "password": password}),
            success: function (msg) {
                location.href = "/";
            },
            error: function (msg) {
                alert("登录失败!!")
            }
        });
    }

    //获取订单信息,通过/api转发到网关,通过/order转发到order微服务
    function getOrder() {
        $.get("/api/order/orders/1", function (data) {
            $("#orderId").val(data.id);
            $("#productId").val(data.productId);
        });
    }

    //退出
    function logout() {
        $.get("/logout",function(){});
        location.href = "/";
    }

</script>
</body>
</html>

  3.1.3、提供登陆方法,使用password模式像认证服务器获取令牌,将原来由http客户端的动作,放到代码中

/**
 * 模拟前端服务器
 *
 * @author caofanqi
 * @date 2020/2/5 16:34
 */
@Slf4j
@RestController
@EnableZuulProxy
@SpringBootApplication
public class WebAppApplication {

    private RestTemplate restTemplate = new RestTemplate();

    public static void main(String[] args) {
        SpringApplication.run(WebAppApplication.class, args);
    }

    /**
     * 获取当前认证的token信息
     */
    @GetMapping("/me")
    public TokenInfoDTO me(HttpServletRequest request){
        return (TokenInfoDTO)request.getSession().getAttribute("token");
    }

    /**
     * 登陆方法
     * 向认证服务器获取令牌,将token信息放到session中
     */
    @PostMapping("/login")
    public void login(@RequestBody UserDTO userDTO, HttpServletRequest request) {

        String oauthTokenUrl = "http://gateway.caofanqi.cn:9010/token/oauth/token";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBasicAuth("webApp", "123456");

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.set("username", userDTO.getUsername());
        params.set("password", userDTO.getPassword());
        params.set("grant_type", "password");
        params.set("scope", "read write");

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers);

        ResponseEntity<TokenInfoDTO> response = restTemplate.exchange(oauthTokenUrl, HttpMethod.POST, httpEntity, TokenInfoDTO.class);

        request.getSession().setAttribute("token", response.getBody());
        log.info("tokenInfo : {}",response.getBody());

    }

    /**
     *  退出
     */
    @RequestMapping("/logout")
    public void logout(HttpServletRequest request){
        request.getSession().invalidate();
    }

}

  3.1.4、通过zuul的转发功能,我们再每个请求头中添加token令牌

/**
 * 将session中的token取出放到请求头中
 *
 * @author caofanqi
 * @date 2020/2/6 0:34
 */
@Component
public class SessionTokenFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        TokenInfoDTO token = (TokenInfoDTO) request.getSession().getAttribute("token");

        if (token != null) {
            requestContext.addZuulRequestHeader("Authorization","bearer " + token.getAccess_token());
        }

        return null;
    }
}

  3.1.5、修改hosts文件,便于区别各服务

  3.1.6、application.yml配置文件,将/api/**的请求都转发到gateway网关服务

spring:
  application:
    name: webApp

server:
  port: 9000

zuul:
  routes:
    api:
      path: /api/**
      url: http://gateway.caofanqi.cn:9010
  sensitive-headers:

  3.2、启动各服务,测试

  3.2.1、访问http://web.caofanqi.cn:9000/,因为没有登陆,所以显示登陆界面

  3.2.2、输入zhangsan,123456,登陆成功

  认证服务器,返回给webApp的token信息

  3.2.3、点击获取订单信息,可以获得订单,SessionTokenFilter会在请求头中添加token,并且请求是以/api开头的会转发到网关,再由网关转发到order服务。

  

  3.2.4、点击退出,又到了登陆页面

项目源码:https://github.com/caofanqi/study-security/tree/dev-web-password

原文地址:https://www.cnblogs.com/caofanqi/p/12268421.html

时间: 2024-10-15 10:21:38

认证和SSO(一)-基于OAuth2单点登陆基本架构的相关文章

认证和SSO(五)-基于token的SSO

1.修改项目使其基于浏览器cookie的SSO 1.1.修改回调方法,获得到token后,由存放到session改为存放到cookie /** * 回调方法 * 接收认证服务器发来的授权码,并换取令牌 * * @param code 授权码 * @param state 请求授权服务器时发送的state */ @GetMapping("/oauth/callback") public void oauthCallback(@RequestParam String code, Strin

两种单点登陆设计

单点登陆设计SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制.它是目前比较流行的企业业务整合的解决方案之一      现在很多企业级应用都基本会去实现单点登陆功能,这样对于用户体验上会有不错的加强.不需要重复登陆多次.好了废话少说,我今天主要介绍两种单点登陆设计. 第一种:最简单的单点登陆设计,如下图: 如图所示:当直接访问各类业务系统时,在页面

Spring Security 解析(六) —— 基于JWT的单点登陆(SSO)开发及原理解析

Spring Security 解析(六) -- 基于JWT的单点登陆(SSO)开发及原理解析 ??在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .Spring Security Oauth2 等权限.认证相关的内容.原理及设计学习并整理一遍.本系列文章就是在学习的过程中加强印象和理解所撰写的,如有侵权请告知. 项目环境: JDK1.8 Spring boot 2.x Spring Security 5.x ?

NET Core 2.0使用Cookie认证实现SSO单点登录

NET Core 2.0使用Cookie认证实现SSO单点登录 之前写了一个使用ASP.NET MVC实现SSO登录的Demo,https://github.com/bidianqing/SSO.Sample,这个Demo是基于.NET Framework,.NET Core 2.0出来了试着使用ASP.NET Core尝试一下.假如我们有三个站点 domain.dev order.domain.dev passport.domain.dev domain.dev作为我们的主站肯定是可以匿名访问

集成基于CAS协议的单点登陆

相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点登陆服务的应用时就不再需要重新登陆了.而CAS协议则正是各单点登陆产品所需要实现的协议,其全称为Central Authentication Service. 那为什么要写这篇博客呢?这是因为在为公司的产品集成SSO的时候,我发现如果软件开发人员不了解CAS协议,那么他在集成出现错误的时候将完全没有

ASP.NET在不同情况下实现单点登陆(SSO)的方法

第一种:同主域但不同子域之间实现单点登陆 Form验证其实是基于身份cookie的验证.客户登陆后,生成一个包含用户身份信息(包含一个ticket)的cookie,这个cookie的名字就是在web.config里Authentication节form设定的name信息,如 <authentication mode="Forms"> <forms loginUrl="login.aspx" name=".ASPXAUTH" pa

集成基于OAuth协议的单点登陆

在之前的一篇文章中,我们已经介绍了如何为一个应用添加对CAS协议的支持,进而使得我们的应用可以与所有基于CAS协议的单点登陆服务通讯.但是现在的单点登陆服务实际上并不全是通过实现CAS协议来完成的.例如Google就使用OAuth协议来管理它的帐户. 相较于CAS协议,OAuth协议不仅仅可以完成对用户凭证的验证,更可以提供权限管理的功能.在这些权限管理功能的支持下,一个应用甚至可以访问其它使用相同OAuth服务的应用的数据,从而完成应用间的交互. OAuth集成示例 现在我们就来看一个通过OA

[认证授权] 3.基于OAuth2的认证(译)

OAuth 2.0 规范定义了一个授权(delegation)协议,对于使用Web的应用程序和API在网络上传递授权决策非常有用.OAuth被用在各钟各样的应用程序中,包括提供用户认证的机制.这导致许多的开发者和API提供者得出一个OAuth本身是一个认证协议的错误结论,并将其错误的使用于此.让我们再次明确的指出: OAuth2.0 不是认证协议. 混乱的根源来自于在认证协议的内部实际上使用了OAuth,开发人员看到OAuth组件并与OAuth流程进行交互,并假设通过简单地使用OAuth,他们就

单点登陆sso实现

需求: 多个bs业务系统,在某个业务系统登陆后,访问其他bs应用系统无需重复登陆. 制约:必须同一浏览器. 解决方案: 关键词:cookie,跨域,sso 环境 l Passport.com 登陆认证服务 l pis.com 病理业务系统 l lis.com 检验业务系统 l  login 拦截器:验证请求是否有令牌,令牌是否合法() l  令牌 ticket 括号内为增强功能 l  用户访问pis.com,拦截器发现无令牌或令牌无效,跳转至passport.com的登陆页面(防止恶意测试密码,