Spring Cloud security services combat micro _5-1_ single sign-on basic infrastructure

 

 

 

The following are common before and after the end of the separation of architecture, to be exact, which is a semi-detached architecture, there are some problems with this architecture:

1, SEO, search engines can only recognize a static HTML resources, climb HTML DOM tree, it is impossible to identify js dynamically rendered content, so if this is the architecture, SEO have problems

2, in the browser page rendering is completed, if the business complex, to the amount of data, the browser might be caused by stress, may Caton.

 

 Given the architectural issues over the front and rear ends of the separation, a separation of the front and rear ends of the structure we want to take is the following framework:

 

The main difference is the Nginx replaced nodeJS, nodejs can achieve all of the features of nginx.

But also provides other functions, such as server-side page rendering, when the browser requests a html resource, nodejs can send api request, on nodejs the good page rendering, then the rendered page to the browser at this time when do SEO, search engine climb is a full page. If some pages do not need SEO, you can still send the page directly api, to render the page in a browser, both of which are supported.

 

 

In this experiment, instead of springboot NodeJS , pages using html js. Before the series article, postman is used as a client application, from now on, will replace the postman with a practical front-end applications.

Overall this architecture is the following:

 Let's start knocking Code

 1, a new application nb-admin

 

 

effect:

When not logged in, display

 

 

 Click to login, enter your user name and password

 

 (Authentication server hardcoded password is 123456 you can)

 

 Login after:

The main code: session in AdminController, the logon request, call the gateway, access token, after obtaining on the front-end server's

@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

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

Guess you like

Origin www.cnblogs.com/lihaoyang/p/12129852.html