春のクラウドセキュリティサービスの戦闘マイクロ_5-2_セッションベースのSSO

認証コードモードに転換のOAuth2認証モードパスワードモードに前、前とセッションベースのマイクロサービスSSO後の分離構造の下端を実現します。ユーザーは、ログインクライアント上のログインページをクリックし、コールバックメソッドコールバッククライアントアプリケーションに認証サーバ、ログインが成功すると、認証サーバへのログインにジャンプし、認証コードを運ぶ、クライアントは認証サーバ交換に認証コードを保持しますaccess_tokenはは、access_tokenはは自分のセッションを維持取得するためのクライアントの後に、ユーザが正常にログインしている前提としています。

 このプロセスの最上部にあるセッションベースのSSO 3つの寿命を持ち、:

  1、クライアントアプリケーションの妥当性のセッションでは、認証サーバに一度どのくらいジャンプを制御します

  2、セッションの認証サーバは、ユーザ名とパスワードを入力するようにユーザにかかる時間有効で、コントロールであります

  3、有効なaccess_tokenは、アクセス制御どのくらいのマイクロサービスに一度ログインします

記事は現在あり、一連の問題は、このような出口のクリックなど、まだですが、クライアントアプリケーションのセッションはセッション認証サーバの失敗を失敗し、ない、ユーザーがログアウトし、認証にリダイレクトログインボタンをクリックし、上記の認証サーバが自動的にクライアントにコールバックしますので、サーバ、認証サーバは、セッションは、失敗していないため、クライアントのパフォーマンスは、直接であるとユーザーに終了ボタンをポイントする印象を与えて、ログインしますが、道をしませんでした。セッションクライアントと認証サーバの有効期限が切れているときにこの問題を解決するには、以下の、アイデアはシンプルで、[終了]ボタンをクリックしてください。コードを書き始めましょう。

ログ処理ロジック

 [終了]ボタンプロセス:

 1、セッション独自のクライアントアプリケーションの障害  

2、セッション認証サーバの障害、

このように、終了ボタンは、プロンプトへのデフォルト認証サーバーである、クライアントセッションに障害が発生した後、再びクリックすると、認証サーバにジャンプされます

  [OK]をクリックし、ページがデフォルトのログイン・ページの認証サーバに残っています:

 ユーザー名(カジュアル)、パスワードを(123456認証サーバは、ハードコード)を入力し、サインインをクリックし、

 会跳转到了认证服务器默认的首页,没有,所以出现了404。

为什么直接在客户端应用点击登录按钮,登录成功后就可以跳回到客户端应用?看一下在客户端应用点击登录按钮的处理:

 里面有一个 redirect_uri 参数,这样的请求,认证服务器在登录成功后,就知道要跳转到redirect_uri  。但是点击退出后出现的登录页面 ,是由【退出】触发的,认证服务器是不知道登录成功后要跳转到admin应用的。所以,要做退出的处理,让认证服务器知道,退出后要跳转到指定的uri,思路就是在退出的请求上,加一个 redirect_uri的参数,重写认证服务器的退出逻辑,退出后跳转到redirect_uri 即可。

请求认证服务器的退出逻辑的请求上,加上 redirect_uri=http://admin.nb.com:8080/index 

 在认证服务器上找到Spring处理退出逻辑的过滤器 org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter :

/**
 * Generates a default log out page.
 *
 * @author Rob Winch
 * @since 5.1
 */
public class DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter {
    private RequestMatcher matcher = new AntPathRequestMatcher("/logout", "GET");

    private Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = request -> Collections
            .emptyMap();

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (this.matcher.matches(request)) {
            renderLogout(request, response);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    private void renderLogout(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        String page =  "<!DOCTYPE html>\n"
                + "<html lang=\"en\">\n"
                + "  <head>\n"
                + "    <meta charset=\"utf-8\">\n"
                + "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
                + "    <meta name=\"description\" content=\"\">\n"
                + "    <meta name=\"author\" content=\"\">\n"
                + "    <title>Confirm Log Out?</title>\n"
                + "    <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
                + "    <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"
                + "  </head>\n"
                + "  <body>\n"
                + "     <div class=\"container\">\n"
                + "      <form class=\"form-signin\" method=\"post\" action=\"" + request.getContextPath() + "/logout\">\n"
                + "        <h2 class=\"form-signin-heading\">Are you sure you want to log out?</h2>\n"
                + renderHiddenInputs(request)
                + "        <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Log Out</button>\n"
                + "      </form>\n"
                + "    </div>\n"
                + "  </body>\n"
                + "</html>";

        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write(page);
    }

    /**
     * Sets a Function used to resolve a Map of the hidden inputs where the key is the
     * name of the input and the value is the value of the input. Typically this is used
     * to resolve the CSRF token.
     * @param resolveHiddenInputs the function to resolve the inputs
     */
    public void setResolveHiddenInputs(
            Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs) {
        Assert.notNull(resolveHiddenInputs, "resolveHiddenInputs cannot be null");
        this.resolveHiddenInputs = resolveHiddenInputs;
    }

    private String renderHiddenInputs(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> input : this.resolveHiddenInputs.apply(request).entrySet()) {
            sb.append("<input name=\"").append(input.getKey()).append("\" type=\"hidden\" value=\"").append(input.getValue()).append("\" />\n");
        }
        return sb.toString();
    }
}

1,重写退出表单源码

这就是处理退出逻辑的过滤器,其中的html就是之前看到的让用户确认退出的页面,在认证服务器项目新建一个一模一样的包,将上边的类copy进去,由于java的类加载机制,自己写的类会优先于spring的类加载,java会加载我们自己写的类,而不加载spring包里的类:

 

重写后的类源码:

package org.springframework.security.web.authentication.ui;

import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;

/**
 * 重写退出逻辑,由于java的类加载机制,会优先执行自己的类,就不加载spring的了
 * 这里有一个默认的 确认退出页面,可以定制
 * 这里注释掉确认退出的提示语,直接写一段js脚本,提交退出表单
 * 从request里获取到退出逻辑携带的 redirect_uri 参数,放入退出表单的隐藏input,
 * 这样在重写退出成功handler时,可以拿出这个参数,做跳转
 * Generates a default log out page.
 *
 * @author Rob Winch
 * @since 5.1
 */
public class DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter {
    private RequestMatcher matcher = new AntPathRequestMatcher("/logout", "GET");

    private Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = request -> Collections
            .emptyMap();

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (this.matcher.matches(request)) {
            renderLogout(request, response);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    private void renderLogout(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        String page =  "<!DOCTYPE html>\n"
                + "<html lang=\"en\">\n"
                + "  <head>\n"
                + "    <meta charset=\"utf-8\">\n"
                + "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
                + "    <meta name=\"description\" content=\"\">\n"
                + "    <meta name=\"author\" content=\"\">\n"
                + "    <title>Confirm Log Out?</title>\n"
                + "    <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
                + "    <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"
                + "  </head>\n"
                + "  <body>\n"
                + "     <div class=\"container\">\n"
                + "      <form id=\"logoutForm\" class=\"form-signin\" method=\"post\" action=\"" + request.getContextPath() + "/logout\">\n"
//                + "        <h2 class=\"form-signin-heading\">Are you sure you want to log out?</h2>\n"
                + renderHiddenInputs(request)
//                + "        <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Log Out</button>\n"
                +  "<input type='hidden' name='redirect_uri' value="+request.getParameter("redirect_uri")+"/>"
                +  "<script>document.getElementById('logoutForm').submit()</script>"
                + "      </form>\n"
                + "    </div>\n"
                + "  </body>\n"
                + "</html>";

        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write(page);
    }

    /**
     * Sets a Function used to resolve a Map of the hidden inputs where the key is the
     * name of the input and the value is the value of the input. Typically this is used
     * to resolve the CSRF token.
     * @param resolveHiddenInputs the function to resolve the inputs
     */
    public void setResolveHiddenInputs(
            Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs) {
        Assert.notNull(resolveHiddenInputs, "resolveHiddenInputs cannot be null");
        this.resolveHiddenInputs = resolveHiddenInputs;
    }

    private String renderHiddenInputs(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> input : this.resolveHiddenInputs.apply(request).entrySet()) {
            sb.append("<input name=\"").append(input.getKey()).append("\" type=\"hidden\" value=\"").append(input.getValue()).append("\" />\n");
        }
        return sb.toString();
    }
}

上述代码中的荧光绿色的是注释掉的两行代码,这两行代码是说显示的让用户确认退出登录的提示,这里给注释掉,让用户看不到退出提醒

上述代码中的荧光黄色的是新添加的代码,给退出登录的表单加了个id,然后在表单里写了个隐藏域,name= redirect_uri,值从request里获取,用于自定义退出成功Handler里,可以重定向到该路径。最后新增一个JavaScript脚本,自动提交表单。

如果你就想给用户一个退出提示,可以重写这个表单的样式。

2,下面自定义退出登录成功处理器

 3,配置退出成功处理器

 实验

启动四个微服务

 

 访问客户端应用 http://admin.nb.com:8080/index/

 

点击去登录,跳转到了认证服务器的登录页面

 

登录成功,回调到客户端应用admin,点击获取订单信息,获取到了订单信息

 

 点击退出登录,先是在客户端应用将session失效,然后再去认证服务器上做退出登录操作()

 

 然后又跳转到了客户端应用的index页

 

 

 总结

本篇解决了上篇遗留的问题(点击退出登录只是在客户端应用做session失效操作,当再次点击登录后,由于认证服务器的session还有效,用户不用输入用户名密码直接就登录了,给人的感觉是没有彻底退出去)。

本节在客户端应用做退出操作的同时,也在认证服务器上将session失效掉,让用户彻底退出登录。思路是在点击【退出登录】按钮的同时做两件事,一是让客户端应用的session失效,然后再发一个请求到认证服务器的 /logout 端点,这是spring OAuth自带的退出登录过滤器,同时并携带一个redirect_uri参数,让认证服务器退出登录之后,知道跳转到客户端应用去。否则认证服务器默认的退出逻辑是,退出后跳转到了认证服务器的首页,由于没有做首页,所以返回了一个404,我们重写了退出登录类org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter ,让退出登录表单自动提交,实现了退出成功handler,重定向到了客户端应用退出时携带过来的redirect_uri。

本篇代码github  : https://github.com/lhy1234/springcloud-security/tree/chapt-5-3-sso-session 如果帮到了你,给个小星星吧

おすすめ

転載: www.cnblogs.com/lihaoyang/p/12149861.html