Spring Cloud微服务安全实战_5-1_单点登录基本架构

下面是常见的前后端分离的架构,准确的说,这是一种半分离的架构,这种架构存在一些问题:

1,SEO,搜索引擎只能识别静态的HTML资源,爬HTML的DOM树,它是没法识别js动态渲染出来的内容的,所以如果是这种架构,SEO会有问题

2,页面渲染是在浏览器完成的,如果业务复杂,要数据量大,可能会对浏览器造成压力,可能会卡顿。

鉴于以上前后端分离的架构存在的问题,我们要搭的一个前后端分离的架构是下面的架构:

主要区别是把Nginx换成了nodeJS,nodejs可以实现nginx的所有的功能。

而且还提供了其他的功能,比如服务端的页面渲染,浏览器在请求一个html资源的时候,nodejs可以发api请求,在nodejs上把页面渲染好,然后把渲染好的页面给浏览器,此时做SEO的时候,搜索引擎爬到的是一个完整的页面。如果某些页面不需要SEO,仍然可以在页面直接发api,在浏览器渲染页面,这两种都支持。

本实验用springboot代替nodejs,页面使用html js。之前的系列文章里,都是使用的postman作为客户端应用的,从今往后,就用实际的前端应用替换postman了。

整体就是下面这种架构:

下面开始敲代码

1,新建一个应用nb-admin

效果:

未登录时候,显示

点击登录,输入用户名密码

(认证服务器写死的,密码是123456即可)

登录后:

主要代码:在  AdminController ,处理登录请求,调用网关,获取token ,获取后放在了 前端服务器的session里

@ResponseBody
    @PostMapping("/login")
    public void login(@RequestParam String username,@RequestParam String password/*@RequestBody Credential credential*/, HttpSession session){

        //认证服务器验token地址 /oauth/check_token 是  spring .security.oauth2的验token端点
        String oauthServiceUrl = "http://localhost:9070/token/oauth/token";

        HttpHeaders headers = new HttpHeaders();//org.springframework.http.HttpHeaders
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);//不是json请求
        //网关的appId,appSecret,需要在数据库oauth_client_details注册
        headers.setBasicAuth("admin","123456");

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

        HttpEntity<MultiValueMap<String,String>> entity = new HttpEntity<>(params,headers);
        ResponseEntity<AccessToken> response = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST, entity, AccessToken.class);

        session.setAttribute("token",response.getBody());
        log.info("token info : {}",response.getBody().toString());
    }

在 index.html 里,进页面就发一个/me 请求,如果能从session获取到token信息(先这么干),说明登录成功:

@GetMapping("/me")
    @ResponseBody
    public AccessToken me(HttpSession session){
        AccessToken accessToken = (AccessToken) session.getAttribute("token");
        return accessToken;
    }

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>欢迎来到sso子系统1</h1>
    <div id="loginTip"></div>
    <p><button onclick="getOrderInfo()">获取订单信息</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>

</body>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>

    function getOrderInfo(){
        $.get("api/order/orders/1",function(data){
            $("#orderId").val(data.id);
            $("#productId").val(data.productId);
        });
    }

    $(document).ready(function(){

        $.get("/me",function(data,status){
            if(data){
                //已登录
                var htm = "已登录,<a href=‘/logout‘>退出</a>";
                $("#loginTip").html(htm);
            }else{
                //未登录
                var href = "<a href=‘/loginPage‘>未登录,去登录</a>";
                $("#loginTip").append(href);
            }
        });
    });

</script>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

    <center>
        <h3>登录</h3>
        <table border="1">
            <tr>
                <td>用户名</td>
                <td><input id="username" name="username" value="lhy"/></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input id="password" name="password" value="123456"/></td>
            </tr>
            <tr>
                <td colspan="2" align="right"><button id="submitBtn" onclick="login()">登录</button></td>
            </tr>
        </table>
    </center>
</body>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    function login() {
        var username = $("#username").val();
        var password = $("#password").val();
        //alert(username+", " +password)

        $.ajax({
            type: "POST",
            url: "/login",
            data: {"username":username, "password":password},
            success: function(msg){
                location.href = "/index";
            },
            error:function(msg){
                alert("登录失败!!")
            }
        });
    }

</script>
</html>

请求转发

在nb-admin上,index.html,加上获取订单信息,点击获取订单按钮,发一个请求到前端服务nb-admin,nb-admin配上zuul网关,让它把这个请求转发到网关去,由网关再去请求订单服务,获取订单信息。

在nb-admin项目加入zuul依赖:

<dependency>
     <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

启动类加上注解:

@EnableZuulProxy

配置zuul路由,以 /api 开头的请求,转发到网关

zuul:
  routes: #路由的配置是个Map,可以配置多个
    api:  #api开头的请求,都转发到网关9070
      url:  http://gateway.nb.com:9070
  sensitive-headers:  null  #设置敏感头设置为空,Authorization等请求头的请求,都往后转发

server:
  port: 8080
spring:
  application:
    name: admin

注:几个项目都配置了host,以方便区分cookie等

127.0.0.1 gateway.nb.com    ----网关

127.0.0.1 admin.nb.com  ----前端服务

127.0.0.1 auth.nb.com    ----认证服务

127.0.0.1 order.nb.com  ----订单服务

网关上,获取订单信息,是需要token的,所以在nb-admin上,需要从session中获取到token,加在请求头里,再发往网关。新建一个zuulFilter,统一在请求头加token。

SessionTokenFilter
package com.nb.security.admin;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * 从session获取token,统一加到请求头中去
 */
@Component
public class SessionTokenFilter extends ZuulFilter {

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

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        AccessToken accessToken = (AccessToken)request.getSession().getAttribute("token");
        if(accessToken != null){
            requestContext.addZuulRequestHeader("Authorization","Bearer "+accessToken.getAccess_token());
        }
        return null;
    }
}

AccessToken

import lombok.Data;

import java.util.Date;

/**
 * access_token
 * Created by: 李浩洋 on 2020-01-02
 **/
@Data
public class AccessToken {

    private String access_token;

    private String token_type;

    private Date expires_in;

    private String scope;
}

现在效果:

登录:

登录后

点击获取订单信息,会发一个  api/order/orders/1 请求,由于nb-admin里配置了zuul路由,api开头的请求都会转发到网关,所以这里实际访问了网关,网关又转发到了order-api,最终返回orderInfo

function getOrderInfo(){
        $.get("api/order/orders/1",function(data){
            $("#orderId").val(data.id);
            $("#productId").val(data.productId);
        });
    }

退出登录

退出登录很简单,直接发一个请求,将nb-admin里的session失效,就行了
@GetMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();
        return "index";
    }

目前已经用 springboot实现了一个前端服务器,实现了oauth协议中 password模式的登录,请求转发,退出。目前还存在不少问题,下节讲解问题并解决。

(其实我认为将这个就当做某个系统的后端服务也没啥问题,类似于你去微信申请客户端应用一样,这里的nb-admin就是我们自己的公众号项目)

代码github:https://github.com/lhy1234/springcloud-security/tree/chapt-5-1-ui

如果对你有一点,就给个小星星吧

原文地址:https://www.cnblogs.com/lihaoyang/p/12129852.html

时间: 2024-07-31 16:45:11

Spring Cloud微服务安全实战_5-1_单点登录基本架构的相关文章

Spring Cloud微服务安全实战_3-8_API安全之登录

前面的文章 https://www.cnblogs.com/lihaoyang/p/11967121.html  说了用过滤器实现HttpBasic 认证 ,在请求头里携带用户名和密码,存在的问题是,你不可能让用户每个请求都输入用户名密码吧,即使前端把用户名密码存起来,这也是不安全的. 一.基于Token的身份认证的原理图 基于Cookie-Session的实现 这种方式有很多种实现方式,在javaweb开发中,最常见的实现方式是,基于 Cookie-Session 的实现,基于session的

Spring Cloud微服务框架 实战企业级优惠券系统

第1章 课程介绍[终于等到你,快来认识我]本章中将对课程中涉及到的技术.工具.业务等进行简单介绍. 第2章 准备工作[工欲善其事,必先利其器]本章中将对课程中使用到的技术工具做介绍,包括Maven.MySQL.Redis.Kafka:会对它们的功能.安装.基本的使用方法进行介绍. 第3章 SpringBoot 开发框架[基础打不牢,学问攀不高]课程主体业务使用SpringCloud框架开发实现,但是SpringCloud基于SpringBoot实现.为便于更顺畅学习,本章中会对SpringBoo

Spring cloud微服务安全实战

第1章 课程导学我们会对整个课程的内容做一个简要的介绍,包括章节的安排,使用的主要技术栈,实战案例的介绍以及前置知识的介绍等内容. 第2章 环境搭建开发工具的介绍及安装,介绍项目代码结构并搭建,基本的依赖和参数设置. 第3章 API安全我们从简单的API场景入手,讲述API安全相关的知识.首先我们会介绍要保证一个API安全都需要考虑哪些问题,然后我们针对这些问题介绍常见的安全机制,我们会针对每种问题和安全机制编写相应的代码,让大家对这些问题和安全机制有一个初步的认识.... 第4章 微服务网关安

Spring cloud微服务安全实战完整教程

本文配套视频教程及资料获取:点击这里 Spring Cloud微服务安全实战 采用流行的微服务架构开发,应用程序访问安全将会面临更多更复杂的挑战,尤其是开发者最关心的三大问题:认证授权.可用性.可视化.本课程从简单的API安全入手,过渡到复杂的微服务场景,解决上述三大问题痛点,并结合实际给出相应解决方案.帮助大家形成对安全问题的系统性思考,实战开发一套可在中小公司落地的完整的安全方案. 学习目标: 技术要点: 环境参数: 本文配套视频教程及资料获取:点击这里 原文地址:https://www.c

Spring cloud微服务安全实战-6-1本章概述

这一章来讲一下,微服务之间的通讯安全. 当前这个架构还存在的问题 在网关上做限流还是有一些问题的.例如我的订单服务限流是100,库存服务限流也是100.但是我的订单服务会调用我的库存服务.那么在网关这,给订单转100个请求,库存转100个请求,最后订单又调了库存,库存会同时受到200个请求.这时候库存服务可能就挂掉了. 这是在网关这里做限流,可能会出现的一些问题. 第二个问题就是身份认证. 效率低,在网关上每一个请求都要去认证服务器验令牌.这样就会导致多一次网络请求的开销.同时我的认证服务器压力

Spring Cloud微服务安全实战_4-5_搭建OAuth2资源服务器

上一篇搭建了一个OAuth2认证服务器,可以生成token,这篇来改造下之前的订单微服务,使其能够认这个token令牌. 本篇针对订单服务要做三件事: 1,要让他知道自己是资源服务器,他知道这件事后,才会在前边加一个过滤器去验令牌(配置@EnableResourceServer 配置类) 2,要让他知道自己是什么资源服务器(配置资源服务器ID) 3,配置去哪里验令牌,怎么验令牌,要带什么信息去验 (配置@EnableWebSecurity 配置TokenServices,配置Authentica

Spring cloud微服务安全实战-4-7重构代码以适应真实环境

现在有了认证服务器,也配置了资源服务器.也根据OAuth协议,基于令牌认证的授权也跑通了.基本的概念也有了简单的理解. 往下深入之前,有几个点,还需要说一下 使用scopes来控制权限,scopes可以理解为之前的ACL 第三章的时候自己写的ACL来控制的读写权限.在OAuth协议里面用scopes来实现ACL的权限控制,两方面,首先在服务器这一端,可以针对不同的应用发出去不同权限的令牌, . 比如针对oderApp可以有读权限,也可以有写的权限. 针对orderService发出去的就只有re

Spring cloud微服务安全实战-5-7实现基于session的SSO(客户端应用的Session有效期)

授权模式改造成了Authorization code完成了改造的同时也实现了SSO.微服务环境下的前后端分离的单点登陆. 把admin的服务重启.刷新页面 并没有让我去登陆,直接就进入了首页. order的API控制台 只要你在认证服务器上的session没过期.认证服务器就知道你是谁,他就不会让你输入用户名密码了.直接跳回到客户端应用. 一共有三个有效期. 退出操作 退出的时候.现在前端服务器清空session,认证服务器也需要清空session 在前端服务器退出后,再跳转到认证服务器执行退出

Spring cloud微服务安全实战-6-2JWT认证之认证服务改造

首先来解决认证的问题. 1.效率低,每次认证都要去认证服务器调一次服务. 2.传递用户身份,在请求头里面, 3.服务之间传递请求头比较麻烦. jwt令牌. spring提供了工具,帮你在微服务之间传递令牌.让你不用去写额外的代码 服务器端的改造 看一下认证服务器配置的这个类.这里有个tokenStore,就是令牌的存储器.现在用的是jdbc的TokenStore,令牌是存在数据库里的 我们new  一个jwtTokenStore它 需要一个参数jwtTokenEnhancer 需要set一个ke

Spring Cloud微服务安全实战_4-10_用spring-cloud-zuul-ratelimit做限流

本篇讲网关上的限流 用开源项目spring-cloud-zuul-ratelimit 做网关上的限流 (项目github:https://github.com/marcosbarbero/) 1,在网关项目里,引入限流组件的maven依赖: 2,在网关项目yml配置里,配限流相关配置 github也有相关配置说明:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit 限流框架限流需要存一些信息,可以存在数据库里,也可以存在red