基于Spring Boot的单点登录

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

 

前言:

    通过对CAS架构的学习,模仿实现了基于Cookie和过滤器的单点登录。并且利用Spring  Boot中的自配置,来移除客户端重复配置。

  • 代码地址:https://gitee.com/marvelcode/marvelcode-sso
  •  Lombok:通过对应的注解,在编译源码的时候生成对应的方法,保证源代码的整洁。常用的有@Getter、@Setter、@Data等。

 

流程图:

基于Spring Boot的单点登录_第1张图片

  • 先大致讲下流程,用户请求service-1,过滤器拦截请求后,从cookie中获取TGT(一个证明用户已登录的票据),如果没有取到,就重定向sso-server的登录页,并传递原本的请求(方便登录成功后重定向回原url);
  • 用户名密码验证通过后,就生成ST(一个与服务绑定的票据,说明用户有权访问该服务)拼接在原url后重定向,并同时生成cookie用于存放TGT(这里用到了共享cookie的技巧);
  • 这时会再次被过滤器拦截,校验ST的合法性,同样是请求sso-server,在验证通过后放行。
  • 如果用户访问service-2,过滤器检测到TGT的存在,就会去sso-server验证TGT,并在验证通过后生成对应服务的ST,同样的重定向...

客户端:

  • 抽象过滤器:
package com.menghao.sso.client.filter;

import com.menghao.sso.client.util.CommonUtils;
import lombok.Setter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * 

客户端过滤器抽象类.
* * @author menghao. * @version 2017/11/15. */ public abstract class AbstractCasFilter implements Filter { protected final Log log = LogFactory.getLog(this.getClass()); /* 请求服务主机名 */ @Setter private String clientHost; /* cas服务端地址 */ @Setter protected String serverHost; private static final String HTTP = "http://"; // ...省略若干构建url方法 protected String makeOriginalRequest(HttpServletRequest request, HttpServletResponse response) { StringBuilder builder = new StringBuilder(); builder.append(request.isSecure() ? "https://" : "http://"); builder.append(clientHost); builder.append(request.getRequestURI()); // 如果存在查询参数,将参数抽取拼接 if (StringUtils.hasLength(request.getQueryString())) { int index = request.getQueryString().indexOf(CommonUtils.ST_ID + "="); // 默认规则ticket放在查询参数最后 if (index == -1) { builder.append("?").append(request.getQueryString()); } else if (index == 0) { // do nothing } else { index = request.getQueryString().indexOf("&" + CommonUtils.ST_ID + "="); if (index == -1) { builder.append("?").append(request.getQueryString()); } else { builder.append("?").append(request.getQueryString().substring(0, index)); } } } final String returnValue = response.encodeURL(builder.toString()); if (log.isDebugEnabled()) { log.debug("serviceUrl make: " + returnValue); } return returnValue; } protected abstract void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException; @Override public void init(FilterConfig filterConfig) throws ServletException { } /* 此步对request和response做了统一转型 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { doFilterInternal((HttpServletRequest) request, (HttpServletResponse) response, chain); } @Override public void destroy() { } }

    抽象的主要目的,是为了对ServletRequest 和 ServletResponse的统一转型。其中代码省略了很多构造url的方法:比如登录、验证、注销等等。其中makeOriginalRequest是获取原本的url请求(过滤掉ServiceTicket后的原本url请求),该方法构造的url会传递给服务端,方便登录成功的重定向。

  • 过滤器一:
package com.menghao.sso.client.filter;


import com.menghao.sso.client.util.CommonUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 

对TGT和ST有无校验.
* * @author menghao. * @version 2017/11/15. */ public class AuthenticationFilter extends AbstractCasFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { Cookie cookie = WebUtils.getCookie(request, CommonUtils.TGT_ID); // 无TGT,说明未登录 if (null == cookie || cookie.getValue() == null) { String originalRequest = makeOriginalRequest(request, response); // 没有ticket则重定向登录 String loginUrl = makeLoginRequest(originalRequest); response.sendRedirect(loginUrl); return; } // 有TGT,无ST,说明已登录但登录其他系统 String serviceTicket = request.getParameter(CommonUtils.ST_ID); if (!StringUtils.hasText(serviceTicket)) { String originalRequest = makeOriginalRequest(request, response); String validateRequest = makeValidateTGTRequest(originalRequest, cookie.getValue()); response.sendRedirect(validateRequest); return; } // 具备TGT和ST filterChain.doFilter(request, response); } }

  • 过滤器二:
package com.menghao.sso.client.filter;

import com.menghao.sso.client.model.ValidateBean;
import com.menghao.sso.client.util.CommonUtils;
import com.menghao.sso.client.validation.TicketValidator;
import com.menghao.sso.client.validation.ValidationException;
import lombok.Setter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 

对ST合法性校验.
* * @author menghao. * @version 2017/11/15. */ public class CheckTicketFilter extends AbstractCasFilter { @Setter private TicketValidator ticketValidator; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { // 创建服务验证请求 String serviceTicket = request.getParameter(CommonUtils.ST_ID); String originalRequest = makeOriginalRequest(request, response); String validateRequest = makeValidateSTRequest(originalRequest); // 发送验证请求 try { ValidateBean validateBean = ValidateBean.builder().url(validateRequest).serviceTicket(serviceTicket).build(); Boolean success = ticketValidator.validate(validateBean); if (success) { filterChain.doFilter(request, response); return; } } catch (ValidationException e) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); log.warn(e, e); throw new ServletException(e); } } }

 

package com.menghao.sso.client.validation;

import com.menghao.sso.client.model.ValidateBean;
import lombok.Setter;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;


/**
 * 

抽象校验类.
* * @author menghao. * @version 2017/11/16. */ public abstract class AbstractTicketValidator implements TicketValidator { @Setter protected RestTemplate restTemplate; @Setter protected String casServerUrl; /** * 模版模式 * * @param validateBean 验证包装类 * @return Boolean 是否验证通过 * @throws ValidationException */ @Override public Boolean validate(ValidateBean validateBean) throws ValidationException { try { ResponseEntity responseEntity = restTemplate.getForEntity(validateBean.getUrl(), Boolean.class, validateBean.getServiceTicket()); return parseResponse(responseEntity); } catch (RestClientException e) { throw new ValidationException(e); } } protected abstract Boolean parseResponse(ResponseEntity responseEntity); }

    介绍完客户端的主要验证方案,来看看如何将客户端以插件的形式“配置”到各个需要验证的模块上。

  • 自配置:

	org.springframework.boot
	spring-boot-configuration-processor
	true

 

package com.menghao.sso.client.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 

客户端配置属性.
* * @author menghao. * @version 2017/11/6. */ @Data @ConfigurationProperties(prefix = "menghao.sso") public class SsoClientProperties { /* 客户端: */ private String clientHost; /* 服务端: */ private String serverHost; /* 限制登录url,逗号分割 */ private String restrictUrls; }

 

package com.menghao.sso.client.config;

import com.menghao.sso.client.filter.AuthenticationFilter;
import com.menghao.sso.client.filter.CheckTicketFilter;
import com.menghao.sso.client.filter.WrapInfoFilter;
import com.menghao.sso.client.validation.PersonTicketValidator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.List;

/**
 * 

客户端自动配置类.
* * @author menghao. * @version 2017/11/16. */ @Configuration @EnableConfigurationProperties(SsoClientProperties.class) @ConditionalOnWebApplication @ConditionalOnProperty(prefix = "menghao.sso", value = "enabled", matchIfMissing = false) public class SsoClientAutoConfiguration { private SsoClientProperties ssoClientProperties; private List urls; public SsoClientAutoConfiguration(SsoClientProperties ssoClientProperties) { this.ssoClientProperties = ssoClientProperties; String strictUrls = ssoClientProperties.getRestrictUrls(); Assert.hasText(ssoClientProperties.getClientHost(), "服务主机地址必须指定"); Assert.hasText(strictUrls, "拦截地址必须指定"); // 初始化时,会将配置需要拦截的url分割成列表 urls = Arrays.asList(strictUrls.split(",")); } @Bean @ConditionalOnMissingBean(AuthenticationFilter.class) public FilterRegistrationBean registerAuthenticationFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); AuthenticationFilter authenticationFilter = new AuthenticationFilter(); authenticationFilter.setServerHost(ssoClientProperties.getServerHost()); authenticationFilter.setClientHost(ssoClientProperties.getClientHost()); filterRegistrationBean.setFilter(authenticationFilter); // 配置过滤器需要拦截的url列表 filterRegistrationBean.setUrlPatterns(urls); filterRegistrationBean.setOrder(1); return filterRegistrationBean; } // ...省略其他过滤器配置 }

  SsoClientProperties封装了一些可配置的信息。

  • @ConfigurationProperties:将application.properties文件中设置的以“menghao.sso”+属性名的属性加载至该Bean,方便自配置时使用。

  SsoClientAutoConfiguration则是真正自配置的实现。

  • @Configuration:将该类映射为XML配置中的标签;
  • @EnableConfigurationProperties:开启对@ConfigurationProperties标记的Bean支持;
  • @ConditionalOnXxx:在满足指定条件下才使得该配置生效。其中多个注解同时标识时是条件与的关系。例如@ConditionalOnProperty(prefix = "menghao.sso", value = "enabled", matchIfMissing = false),就是在“menghao.sso.enabled”属性未设置的情况下配置不生效,即客户端默认不开启;
  •  @Bean:通过标识方法,将方法返回的Bean交由Spring管理,等同于XML配置的标签。

    最后一步,在路径/resources/META-INF路径下,创建spring.factories文件,并添加:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.menghao.sso.client.config.SsoClientAutoConfiguration

    这样,将客户端打成Jar包,并在需要的模块引入依赖,就可以在Spring Boot启动时,自动加载该配置类(前提是满足@ConditionOnXxx的条件)

    如果有额外的属性需要指定,可以通过additional-spring-configuration-metadata.json文件中声明,该文件与spring.factories在同一级目录,格式如下:

{
  "properties": [
    {
      "name": "menghao.sso.enabled",
      "type": "java.lang.Boolean",
      "description": "自定义单点登录客户端.",
      "defaultValue": false
    }
  ]
}
  • 配置属性: 

    只需要在需要的模块引入即可,指定服务端,客户端地址,拦截的url请求正则。

# true开启,false关闭
menghao.sso.enabled = true
menghao.sso.cas-server-host = localhost:1000
menghao.sso.cas-client-host = localhost:1001
# 配置拦截的url,多个用逗号分割
menghao.sso.restrict-urls = /*

 

服务端:

    其中使用到了两种票据,TicketGrantingTicket (TGT)和 ServiceTicket(ST)。在登录成功时,往Cookie中放入TGT,在url上拼接ST;在校验时,如果有ST,直接校验,如果没有则获取TGT,校验通过后授予ST,一切校验失败的行为都会抛出ValidateFailException异常(自定义),并交由异常统一处理返回登录界面。

  • 验证与授权:    

    来看下主要的两个业务实现类:

package com.menghao.sso.server.service;

import com.menghao.sso.server.exception.ValidateFailException;
import com.menghao.sso.server.model.Service;
import com.menghao.sso.server.model.credentials.Credentials;
import com.menghao.sso.server.model.credentials.UsernamePasswordCredentials;
import com.menghao.sso.server.model.ticket.ServiceTicket;
import com.menghao.sso.server.model.ticket.TicketGrantingTicket;
import com.menghao.sso.server.registry.TicketRegistry;
import com.menghao.sso.server.repository.UCredentialsRepository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 

认证Service实现.
* * @author menghao. * @version 2017/11/17. */ @org.springframework.stereotype.Service public class AuthenticationServiceImpl implements AuthenticationService { private final Log log = LogFactory.getLog(this.getClass()); @Autowired private UCredentialsRepository uCredentialsRepository; @Autowired private TicketRegistry ticketRegistry; @Override public void validateCredentials(Credentials credentials) throws ValidateFailException { if (credentials instanceof UsernamePasswordCredentials) { UsernamePasswordCredentials usernamePasswordCredentials = uCredentialsRepository.queryByProperty((UsernamePasswordCredentials) credentials); // 验证通过 if (usernamePasswordCredentials == null) { throw new ValidateFailException("用户名与密码不匹配"); } } else { throw new ValidateFailException("暂不支持的认证方式"); } } @Override public void validateGrantingTicket(String ticketGrantingTicketId) throws ValidateFailException { if (ticketGrantingTicketId == null) { throw new ValidateFailException("未检测到票据信息,请登录"); } final TicketGrantingTicket ticketGrantingTicket = (TicketGrantingTicket) this.ticketRegistry.getTicket(ticketGrantingTicketId); if (ticketGrantingTicket == null) { log.debug("TicketGrantingTicket [" + ticketGrantingTicket + "] does not exist."); throw new ValidateFailException("票据信息校验未通过,请登录"); } if (ticketGrantingTicket.isExpired()) { log.debug("ServiceTicket [" + ticketGrantingTicket + "] has expired."); this.ticketRegistry.deleteTicket(ticketGrantingTicketId); throw new ValidateFailException("身份验证已过期,请重新登录"); } ticketGrantingTicket.updateLastTimeUsed(); } @Override public void validateServiceTicket(String serviceTicketId, Service service) throws ValidateFailException { if (serviceTicketId == null || service == null) { throw new ValidateFailException("未检测到票据信息,请登录"); } final ServiceTicket serviceTicket = (ServiceTicket) this.ticketRegistry.getTicket(serviceTicketId); if (serviceTicket == null) { log.debug("ServiceTicket [" + serviceTicketId + "] does not exist."); throw new ValidateFailException("票据信息校验未通过,请登录"); } if (serviceTicket.isExpired()) { log.debug("ServiceTicket [" + serviceTicketId + "] has expired."); this.ticketRegistry.deleteTicket(serviceTicketId); throw new ValidateFailException("身份验证已过期,请重新登录"); } serviceTicket.incrementCountOfUses(); serviceTicket.updateLastTimeUsed(); if (serviceTicket.isExpired()) { log.debug("ServiceTicket [" + serviceTicketId + "] has expired."); this.ticketRegistry.deleteTicket(serviceTicketId); throw new ValidateFailException("身份验证已过期,请重新登录"); } if (!service.equals(serviceTicket.getService())) { log.debug("ServiceTicket [" + serviceTicketId + "] does not match supplied service."); throw new ValidateFailException("票据信息与服务不匹配,请登录"); } } }

 

package com.menghao.sso.server.service;

import com.menghao.sso.server.exception.InvalidTicketException;
import com.menghao.sso.server.exception.ValidateFailException;
import com.menghao.sso.server.model.Principal;
import com.menghao.sso.server.model.Service;
import com.menghao.sso.server.model.SimplePrincipal;
import com.menghao.sso.server.model.credentials.Credentials;
import com.menghao.sso.server.model.credentials.UsernamePasswordCredentials;
import com.menghao.sso.server.model.ticket.ServiceTicket;
import com.menghao.sso.server.model.ticket.TicketGrantingTicket;
import com.menghao.sso.server.model.ticket.TicketGrantingTicketImpl;
import com.menghao.sso.server.model.validation.Authentication;
import com.menghao.sso.server.registry.ExpirationPolicy;
import com.menghao.sso.server.registry.TicketRegistry;
import com.menghao.sso.server.util.TicketIdGenerator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.HashMap;

/**
 * 

授权Service实现.
* * @author menghao. * @version 2017/11/17. */ @org.springframework.stereotype.Service public class AuthorizationServiceImpl implements AuthorizationService { private final Log log = LogFactory.getLog(this.getClass()); @Autowired private TicketRegistry ticketRegistry; @Autowired private ExpirationPolicy expirationPolicy; @Override public String createTicketGrantingTicket(Credentials credentials) throws ValidateFailException { if (credentials instanceof UsernamePasswordCredentials) { String username = ((UsernamePasswordCredentials) credentials).getUsername(); if (!StringUtils.hasText(username)) { throw new ValidateFailException("无法获取用户名"); } String tgtId = TicketIdGenerator.newTGTId(); Principal principal = new SimplePrincipal(username); Authentication authentication = new Authentication(principal, new HashMap()); ticketRegistry.addTicket(new TicketGrantingTicketImpl(tgtId, authentication, this.expirationPolicy)); return tgtId; } return null; } @Override public String createServiceTicket(String ticketGrantingTicketId, Service service) throws ValidateFailException { if (!StringUtils.hasText(ticketGrantingTicketId)) { throw new ValidateFailException("未检测到票据信息,请登录"); } TicketGrantingTicket ticketGrantingTicket = (TicketGrantingTicket) ticketRegistry.getTicket(ticketGrantingTicketId); if (ticketGrantingTicket == null) { throw new ValidateFailException("票据信息校验未通过,请登录"); } if (ticketGrantingTicket.isExpired()) { throw new ValidateFailException("身份验证已过期,请重新登录"); } final ServiceTicket serviceTicket = ticketGrantingTicket.grantServiceTicket( TicketIdGenerator.newSTId(), service, this.expirationPolicy); this.ticketRegistry.addTicket(serviceTicket); log.info("Granted service ticket [" + serviceTicket.getId() + "] for service [" + service.getUrl() + "] for user [" + serviceTicket.getGrantingTicket().getAuthentication() .getPrincipal().getUrl() + "]"); return serviceTicket.getId(); } @Override public void destroyTicketGrantingTicket(String ticketGrantingTicketId) throws InvalidTicketException { if (!StringUtils.hasText(ticketGrantingTicketId)) { throw new InvalidTicketException(); } ticketRegistry.deleteTicket(ticketGrantingTicketId); } }

    这两个类中几乎包含了所有的服务端处理逻辑,Controller层就是借助这两个类的方法,拼装后实现的。其中注入的 TicketRegistry(票据注册)和 ExpirationPolicy(票据过期)代码讲解在下面。

  • 异常统一处理:
package com.menghao.sso.server.exception;

import lombok.Getter;

/**
 * 

校验失败异常.
* * @author menghao. * @version 2017/11/20. */ public class ValidateFailException extends Exception { public ValidateFailException(String msg) { super(); this.msg = msg; } public ValidateFailException(String service, String msg) { this(msg); this.service = service; } @Getter private String msg; @Getter private String service; }

 

package com.menghao.sso.server.controller.advice;

import com.menghao.sso.server.exception.ValidateFailException;
import com.menghao.sso.server.util.CommonUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * 

异常统一处理类.
* * @author menghao. * @version 2017/11/20. */ @ControllerAdvice public class ExceptionController { @ExceptionHandler(ValidateFailException.class) public ModelAndView validateException(ValidateFailException e) throws UnsupportedEncodingException { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", URLEncoder.encode(e.getMsg(), "UTF-8")); modelAndView.addObject(CommonUtils.SERVICE, e.getService()); modelAndView.setViewName("redirect:" + CommonUtils.LOGIN_URL); return modelAndView; } }

    除了验证通过的其他任何情况,如过期,不存在等等,都会抛出 ValidateFailException 异常,通过异常统一处理,重定向至登录页,并将提示信息封装到 request 域中。

  • 票据注册:

    为了方便横向扩展,将注册策略抽象为接口,目前只实现了一种:

package com.menghao.sso.server.registry;

import com.menghao.sso.server.model.ticket.Ticket;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 

默认的票据注册实现.
* * @author menghao. * @version 2017/11/21. */ public final class DefaultTicketRegistry implements TicketRegistry { private final Log log = LogFactory.getLog(getClass()); private final Map cache = new HashMap(); public synchronized void addTicket(final Ticket ticket) { if (ticket == null) { throw new IllegalArgumentException("ticket cannot be null"); } log.debug("Added ticket [" + ticket.getId() + "] to registry."); this.cache.put(ticket.getId(), ticket); } public synchronized Ticket getTicket(final String ticketId) { log.debug("Attempting to retrieve ticket [" + ticketId + "]"); final Ticket ticket = this.cache.get(ticketId); if (ticket != null) { log.debug("Ticket [" + ticketId + "] found in registry."); } return ticket; } public synchronized boolean deleteTicket(final String ticketId) { log.debug("Removing ticket [" + ticketId + "] from registry"); return (this.cache.remove(ticketId) != null); } public synchronized Collection getTickets() { return Collections.unmodifiableCollection(this.cache.values()); } }

  • 票据过期:

    为了方便横向扩展,将过期策略抽象为接口,目前只实现了两种:

package com.menghao.sso.server.registry;

import com.menghao.sso.server.model.ticket.Ticket;

/**
 * 基于过期时间:最近一次使用的使用
 */
public final class TimeoutExpirationPolicy implements ExpirationPolicy {

    private final long timeToKillInMilliSeconds;

    public TimeoutExpirationPolicy(final long timeToKillInMilliSeconds) {
        this.timeToKillInMilliSeconds = timeToKillInMilliSeconds;
    }

    public boolean isExpired(final Ticket ticket) {
        return (ticket == null) || (System.currentTimeMillis() - ticket.getLastTimeUsed() >= this.timeToKillInMilliSeconds);
    }
}

 

package com.menghao.sso.server.registry;

import com.menghao.sso.server.model.ticket.Ticket;

/**
 * 永不过期
 */
public final class NeverExpirationPolicy implements ExpirationPolicy {

    public boolean isExpired(final Ticket ticket) {
        return false;
    }
}
  • 自配置:
package com.menghao.sso.server.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 

服务端配置属性Bean.
* * @author menghao. * @version 2017/11/17. */ @Data @ConfigurationProperties(prefix = "menghao.sso.server") public class SsoServerProperties { /* 缓存策略 */ private String ticketCache = "default"; }

 

package com.menghao.sso.server.config;

import com.menghao.sso.server.registry.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 

服务端自配置类
* * @author menghao. * @version 2017/11/17. */ @Configuration public class SsoServerAutoConfiguration { @Bean @ConditionalOnProperty(prefix = "menghao.sso.server", name = "ticketCache", havingValue = "default") public TicketRegistry defaultRegistry() { return new DefaultTicketRegistry(); } @Bean public ExpirationPolicy expirationPolicy() { return new TimeoutExpirationPolicy(1000 * 60 * 60); } }

    默认注册策略,采用内存放置Map的形式,存储 ticketId-Ticket键值对。默认的过期策略,1小时的间隔使用时间。

 

总结:

    目前只完成了单个请求的校验逻辑,如果是服务间调用,按照Cas原本架构中,是以代理票据实现的,目前还不能支持。该架构只是为了能够对Cas架构有更好的理解,而进行的拆分整理,单纯的实现了基本功能,对于并发等情况未做考虑。

转载于:https://my.oschina.net/marvelcode/blog/1576104

你可能感兴趣的:(基于Spring Boot的单点登录)