<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          單點(diǎn)登錄實(shí)現(xiàn)demo!

          共 36132字,需瀏覽 73分鐘

           ·

          2022-08-26 13:26

          文章目錄

          • 前言
          • 一、CAS是什么?
          • 二、搭建客戶端系統(tǒng)
            1. 引入CAS
            2. 客戶端后端搭建
          • 總結(jié)

          前言

          什么是單點(diǎn)登錄?單點(diǎn)登錄全稱Single Sign On(以下簡稱SSO),是指在多系統(tǒng)應(yīng)用群中登錄一個(gè)系統(tǒng),便可在其他所有系統(tǒng)中得到授權(quán)而無需再次登錄,包括單點(diǎn)登錄與單點(diǎn)注銷兩部分,如圖(不標(biāo)準(zhǔn),只是方便理解)。

          一、CAS是什么?

          CAS 是 Yale 大學(xué)發(fā)起的一個(gè)開源項(xiàng)目,旨在為 Web 應(yīng)用系統(tǒng)提供一種可靠的單點(diǎn)登錄方法,CAS 在 2004 年 12 月正式成為 JA-SIG 的一個(gè)項(xiàng)目。CAS 具有以下特點(diǎn):

          • 開源的企業(yè)級(jí)單點(diǎn)登錄解決方案。
          • CAS Server 為需要獨(dú)立部署的 Web 應(yīng)用。
          • CAS Client 支持非常多的客戶端(這里指單點(diǎn)登錄系統(tǒng)中的各個(gè) Web 應(yīng)用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

          二、搭建客戶端系統(tǒng)

          1.引入CAS

          參考:https://www.bilibili.com/video/BV1xy4y1r7BU?t=666&p=8

          注意其中將證書導(dǎo)入jdk中,一定要注意精確到cacerts這個(gè)文件下,不然一直報(bào)拒絕寫入,另外最好用管理員下的命令窗口

          2.客戶端后端搭建

          1.添加依賴

          <dependency>
              <groupId>org.jasig.cas.client</groupId>
              <artifactId>cas-client-core</artifactId>
              <version>3.3.2</version>
          </dependency>
          <dependency>
              <groupId>joda-time</groupId>
              <artifactId>joda-time</artifactId>
              <version>2.10.5</version>
          </dependency>

          <dependency>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-cas</artifactId>
          </dependency>

          <dependency>
              <groupId>org.springframework.security</groupId>
              <artifactId>spring-security-taglibs</artifactId>
          </dependency>

          2.配置客戶端

          server:
            port: 1234

          3.添加config(filter)文件

          地址全為ip,如果用hosts映射地址,可能會(huì)出現(xiàn)問題

          package com.casclient1.cas.config;

          import org.jasig.cas.client.session.SingleSignOutFilter;
          import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
          import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.beans.factory.InitializingBean;
          import org.springframework.boot.web.servlet.FilterRegistrationBean;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.core.annotation.Order;
          import org.springframework.web.filter.CharacterEncodingFilter;

          import javax.servlet.Filter;
          import java.io.Serializable;
          import java.util.ArrayList;
          import java.util.List;


          @Configuration
          public class FilterConfig implements SerializableInitializingBean {

              private static final Logger LOGGER = LoggerFactory.getLogger(FilterConfig.class);

              public static final String CAS_SIGNOUT_FILTER_NAME = "CAS Single Sign Out Filter";
              public static final String CAS_AUTH_FILTER_NAME = "CAS Filter";
              public static final String CAS_IGNOREL_SSL_FILTER_NAME = "CAS Ignore SSL Filter";
              public static final String CAS_FILTER_NAME = "CAS Validation Filter";
              public static final String CAS_WRAPPER_NAME = "CAS HttpServletRequest Wrapper Filter";
              public static final String CAS_ASSERTION_NAME = "CAS Assertion Thread Local Filter";
              public static final String CHARACTER_ENCODING_NAME = "Character encoding Filter";

           //CAS服務(wù)器退出地址
              private static String casSigntouServerUrlPrefix = "https://127.0.0.1:8443/cas/logout";
              //CAS服務(wù)器登錄地址
              private static String casServerLoginUrl = "https://127.0.0.1:8443/cas/login";
              //客戶端地址
              private static String clienthosturl="http://127.0.0.1:1234";
              //CAS服務(wù)器地址
              private static String casValidationServerUrlPrefix = "https://127.0.0.1:8443/cas";

              public FilterConfig() {

              }

              /**
               * 單點(diǎn)登出功能,放在其他filter之前
               * casSigntouServerUrlPrefix為登出前綴:https://123.207.122.156:8081/cas/logout
               *
               * @return
               */

              @Bean
              @Order(0)
              public FilterRegistrationBean getCasSignoutFilterRegistrationBean() {
                  FilterRegistrationBean registration = new FilterRegistrationBean();
                  registration.setFilter(getCasSignoutFilter());
                  registration.addUrlPatterns("/*""*.html");
                  registration.addInitParameter("casServerUrlPrefix", casSigntouServerUrlPrefix);
                  registration.setName(CAS_SIGNOUT_FILTER_NAME);
                  registration.setEnabled(true);
                  return registration;
              }

              @Bean(name = CAS_SIGNOUT_FILTER_NAME)
              public Filter getCasSignoutFilter() {
                  return new SingleSignOutFilter();
              }

              /**
               * 忽略SSL認(rèn)證
               *
               * @return
               */

              @Bean
              @Order(1)
              public FilterRegistrationBean getCasSkipSSLValidationFilterRegistrationBean() {
                  FilterRegistrationBean registration = new FilterRegistrationBean();
                  registration.setFilter(getCasSkipSSLValidationFilter());
                  registration.addUrlPatterns("/*""*.html");
                  registration.setName(CAS_IGNOREL_SSL_FILTER_NAME);
                  registration.setEnabled(true);
                  return registration;
              }

              @Bean(name = CAS_IGNOREL_SSL_FILTER_NAME)
              public Filter getCasSkipSSLValidationFilter() {
                  return new IgnoreSSLValidateFilter();
              }

              /**
               * 負(fù)責(zé)用戶的認(rèn)證
               * casServerLoginUrl:https://123.207.122.156:8081/cas/login
               * casServerName:https://123.207.122.156:8080/tdw/alerts/
               *
               * @return
               */

              @Bean
              @Order(2)
              public FilterRegistrationBean getCasAuthFilterRegistrationBean() {
                  FilterRegistrationBean registration = new FilterRegistrationBean();
                  final Filter casAuthFilter = getCasAuthFilter();
                  registration.setFilter(casAuthFilter);
                  registration.addUrlPatterns("/*""*.html");
                  registration.addInitParameter("casServerLoginUrl", casServerLoginUrl);
                  registration.addInitParameter("serverName", clienthosturl);
                  registration.setName(CAS_AUTH_FILTER_NAME);
                  registration.setEnabled(true);
                  return registration;
              }

              @Bean(name = CAS_AUTH_FILTER_NAME)
              public Filter getCasAuthFilter() {
                  return new MyAuthenticationFilter();
              }

              /**
               * 對(duì)Ticket進(jìn)行校驗(yàn)
               * casValidationServerUrlPrefix要用內(nèi)網(wǎng)ip
               * casValidationServerUrlPrefix:https://123.207.122.156:8081/cas
               * casServerName:https://123.207.122.156:8080/tdw/alerts/
               *
               * @return
               */

              @Bean
              @Order(3)
              public FilterRegistrationBean getCasValidationFilterRegistrationBean() {
                  FilterRegistrationBean registration = new FilterRegistrationBean();
                  final Filter casValidationFilter = getCasValidationFilter();
                  registration.setFilter(casValidationFilter);
                  registration.addUrlPatterns("/*""*.html");
                  registration.addInitParameter("casServerUrlPrefix", casValidationServerUrlPrefix);
                  registration.addInitParameter("serverName", clienthosturl);
                  registration.setName(CAS_FILTER_NAME);
                  registration.setEnabled(true);
                  return registration;
              }

              @Bean(name = CAS_FILTER_NAME)
              public Filter getCasValidationFilter() {
                  return new Cas20ProxyReceivingTicketValidationFilter();
              }

              /**
               * 設(shè)置response的默認(rèn)編碼方式:UTF-8。
               *
               * @return
               */

              @Bean
              @Order(4)
              public FilterRegistrationBean getCharacterEncodingFilterRegistrationBean() {
                  FilterRegistrationBean registration = new FilterRegistrationBean();
                  registration.setFilter(getCharacterEncodingFilter());
                  registration.addUrlPatterns("/*""*.html");
                  registration.setName(CHARACTER_ENCODING_NAME);
                  registration.setEnabled(true);
                  return registration;
              }

              @Bean(name = CHARACTER_ENCODING_NAME)
              public Filter getCharacterEncodingFilter() {
                  CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
                  characterEncodingFilter.setEncoding("UTF-8");
                  return characterEncodingFilter;
              }

              @Bean
              public FilterRegistrationBean casHttpServletRequestWrapperFilter(){
                  FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
                  authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
                  authenticationFilter.setOrder(6);
                  List<String> urlPatterns = new ArrayList<>();
                  urlPatterns.add("/*");
                  authenticationFilter.setUrlPatterns(urlPatterns);
                  return authenticationFilter;
              }

              @Override
              public void afterPropertiesSet() throws Exception {

              }
          }

          4.filter類中的MyAuthenticationFilter是重寫cas jar包中的AuthenticationFilter,原因是CAS源碼無法認(rèn)證直接重定向,而ajax請(qǐng)求又不能直接重定向,導(dǎo)致前端302,而302vue response攔截器是攔截不到的。所以就想到不讓cas給我重定向,給我返回狀態(tài)碼,告訴前端認(rèn)證失敗,讓前端直接跳轉(zhuǎn)cas服務(wù)器登錄地址。

          修改cas源碼過濾器,復(fù)制源碼AuthenticationFilter這個(gè)過濾器,重寫他,其實(shí)這里只改了重定向的代碼其他都一樣。上MyAuthenticationFilter代碼

          package com.casclient1.cas.config;

          import org.jasig.cas.client.authentication.*;
          import org.jasig.cas.client.util.AbstractCasFilter;
          import org.jasig.cas.client.util.CommonUtils;
          import org.jasig.cas.client.util.ReflectUtils;
          import org.jasig.cas.client.validation.Assertion;

          import javax.servlet.FilterConfig;
          import javax.servlet.*;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.http.HttpSession;
          import java.io.IOException;
          import java.util.HashMap;
          import java.util.Map;

          public class MyAuthenticationFilter extends AbstractCasFilter {
              private String casServerLoginUrl;
              private boolean renew = false;
              private boolean gateway = false;
              private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
              private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
              private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
              private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES = new HashMap();

              public MyAuthenticationFilter() {
              }

              @Override
              protected void initInternal(FilterConfig filterConfig) throws ServletException {
                  if (!this.isIgnoreInitConfiguration()) {
                      super.initInternal(filterConfig);
                      this.setCasServerLoginUrl(this.getPropertyFromInitParams(filterConfig, "casServerLoginUrl", (String)null));
                      this.logger.trace("Loaded CasServerLoginUrl parameter: {}"this.casServerLoginUrl);
                      this.setRenew(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "renew""false")));
                      this.logger.trace("Loaded renew parameter: {}"this.renew);
                      this.setGateway(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "gateway""false")));
                      this.logger.trace("Loaded gateway parameter: {}"this.gateway);
                      String ignorePattern = this.getPropertyFromInitParams(filterConfig, "ignorePattern", (String)null);
                      this.logger.trace("Loaded ignorePattern parameter: {}", ignorePattern);
                      String ignoreUrlPatternType = this.getPropertyFromInitParams(filterConfig, "ignoreUrlPatternType""REGEX");
                      this.logger.trace("Loaded ignoreUrlPatternType parameter: {}", ignoreUrlPatternType);
                      if (ignorePattern != null) {
                          Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = (Class)PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
                          if (ignoreUrlMatcherClass != null) {
                              this.ignoreUrlPatternMatcherStrategyClass = (UrlPatternMatcherStrategy) ReflectUtils.newInstance(ignoreUrlMatcherClass.getName(), new Object[0]);
                          } else {
                              try {
                                  this.logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType);
                                  this.ignoreUrlPatternMatcherStrategyClass = (UrlPatternMatcherStrategy)ReflectUtils.newInstance(ignoreUrlPatternType, new Object[0]);
                              } catch (IllegalArgumentException var6) {
                                  this.logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, var6);
                              }
                          }

                          if (this.ignoreUrlPatternMatcherStrategyClass != null) {
                              this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
                          }
                      }

                      String gatewayStorageClass = this.getPropertyFromInitParams(filterConfig, "gatewayStorageClass", (String)null);
                      if (gatewayStorageClass != null) {
                          this.gatewayStorage = (GatewayResolver)ReflectUtils.newInstance(gatewayStorageClass, new Object[0]);
                      }

                      String authenticationRedirectStrategyClass = this.getPropertyFromInitParams(filterConfig, "authenticationRedirectStrategyClass", (String)null);
                      if (authenticationRedirectStrategyClass != null) {
                          this.authenticationRedirectStrategy = (AuthenticationRedirectStrategy)ReflectUtils.newInstance(authenticationRedirectStrategyClass, new Object[0]);
                      }
                  }

              }

              @Override
              public void init() {
                  super.init();
                  CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
              }

              @Override
              public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                  HttpServletRequest request = (HttpServletRequest)servletRequest;
                  HttpServletResponse response = (HttpServletResponse)servletResponse;
                  if (this.isRequestUrlExcluded(request)) {
                      this.logger.debug("Request is ignored.");
                      filterChain.doFilter(request, response);
                  } else {
                      HttpSession session = request.getSession(false);
                      Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
                      if (assertion != null) {
                          filterChain.doFilter(request, response);
                      } else {
                          String serviceUrl = this.constructServiceUrl(request, response);
                          String ticket = this.retrieveTicketFromRequest(request);
                          boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
                          if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
                              this.logger.debug("no ticket and no assertion found");
                              String modifiedServiceUrl;
                              if (this.gateway) {
                                  this.logger.debug("setting gateway attribute in session");
                                  modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
                              } else {
                                  modifiedServiceUrl = serviceUrl;
                              }

                              this.logger.debug("Constructed service url: {}", modifiedServiceUrl);

                              String xRequested =request.getHeader("x-requested-with");
                              if("XMLHttpRequest".equals(xRequested)){
                                  response.getWriter().write("{\"code\":202, \"msg\":\"no ticket and no assertion found\"}");
                              }else{
                                  String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
                                  this.logger.debug("redirecting to \"{}\"", urlToRedirectTo);
                                  this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
                              }
                          } else {
                              filterChain.doFilter(request, response);
                          }
                      }
                  }
              }

              public final void setRenew(boolean renew) {
                  this.renew = renew;
              }

              public final void setGateway(boolean gateway) {
                  this.gateway = gateway;
              }

              public final void setCasServerLoginUrl(String casServerLoginUrl) {
                  this.casServerLoginUrl = casServerLoginUrl;
              }

              public final void setGatewayStorage(GatewayResolver gatewayStorage) {
                  this.gatewayStorage = gatewayStorage;
              }

              private boolean isRequestUrlExcluded(HttpServletRequest request) {
                  if (this.ignoreUrlPatternMatcherStrategyClass == null) {
                      return false;
                  } else {
                      StringBuffer urlBuffer = request.getRequestURL();
                      if (request.getQueryString() != null) {
                          urlBuffer.append("?").append(request.getQueryString());
                      }

                      String requestUri = urlBuffer.toString();
                      return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri);
                  }
              }

              static {
                  PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
                  PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
                  PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
              }
          }

          測試Controller

          package com.casclient1.cas.controller;

          import com.casclient1.cas.domain.UserDomain;
          import com.casclient1.cas.tools.Result;
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.ResponseBody;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.Console;
          import java.io.IOException;

          @Controller
          public class TestController {
              /**
               * 測試
               * @return
               */

              @GetMapping("/test")
              @ResponseBody
              public Result<UserDomain>  login(HttpServletRequest httpServletRequest){
                  System.out.println("sss");
                  return new Result<>(new UserDomain(httpServletRequest.getRemoteUser()));
              }

              @GetMapping("/checkTicket")
              public void index(HttpServletResponse response) throws IOException {
                  // 前端頁面地址
                  response.sendRedirect("http://127.0.0.1:8088/Home");
              }
                  /**
               * 注銷
               * @return
               */

              @RequestMapping("/logout")
              public String logout(){
                  return "redirect:https://127.0.0.1:8443/cas/logout";
              }
              }

          3.前端

          <template xmlns="http://www.w3.org/1999/html">
            <div >

              <header style="height: 60px">
                <span>客戶端2驗(yàn)證:{{name}}</span>
                  <button @click="logout">安全退出</button>
              </header>
              <router-view></router-view>
              <!--

            <my-vue v-bind:lineID="lineID"></my-vue>-->

            </div>

          </template>
          <style lang="scss">
          </
          style>
          <script type="text/ecmascript-6">

            export default {

              data() {

                return {
                      name:'ss'
                }

              },
          mounted(){
              var _this = this;
              this.$http.get('/test', {headers: {'x-requested-with''XMLHttpRequest'}})
                      .then(function (response{
                          console.log("sss");
                          if (response.data.code === 202) {
                              debugger
                              console.log("sss");
                              window.location.href = "http://127.0.0.1:1235/checkTicket"
                          } else if (response.data.code === 200) {
                              console.log("sss");
                              _this.name = response.data.data.name
                          }
                          console.log(response);
                      })
                      .catch(function (error{
                          console.log(error);
                      });
                  },
                methods: {
                    logout() {
                        window.location.href = "http://127.0.0.1:1234/logout"

                    },
                }
            }
          </script>

          5.效果

          未登錄:

          點(diǎn)擊客戶端1超鏈接

          登錄成功

          點(diǎn)擊客戶端2超鏈接,直接進(jìn)入,無需登錄

          退出

          總結(jié)

          網(wǎng)上有很多CAS單點(diǎn)登錄的demo,但是對(duì)于前后端分離講的比較詳細(xì)的很少,前后端分離,必定會(huì)出現(xiàn)跨域,導(dǎo)致CAS登錄無法重定向等等原因,結(jié)合和網(wǎng)上一些想法和部門代碼后,大致做了一個(gè)比較完善,但很基礎(chǔ)的單點(diǎn)登錄系統(tǒng),當(dāng)然單點(diǎn)登錄不光有CAS,還有JWT(1.所有服務(wù)靠約定來生成token,2.要么集中生成集中判斷,所有服務(wù)都能生成都認(rèn)這個(gè),要么一個(gè)服務(wù)管控全局),OAuth2等等。


          來源:blog.csdn.net/weixin_43483911/article/details/117811270

          瀏覽 44
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  夜夜撸夜夜干 | www男人的天堂 | 黄色成人片| 成人网站视频 | 色狠久久AV翔田千里 |