springboot security 集成 cas 问题 No subject alternative names present
创始人
2024-01-29 19:16:00
0

springboot security 集成 cas 问题 No subject alternative names present

  • 前言
  • 一、问题
    • 1.实际问题
  • 二、大海捞针
    • 1.星星之火
    • 2.通用方法
    • 啰嗦一句
  • 解决
    • 2.新建三个类
    • 配置文件修改


前言

场景:
在一次springboot security 集成 cas开发中,代码报错:java.security.cert.CertificateException: No subject alternative names present。
环境:
springboot security 集成 cas,cas 服务是其他厂家开发,使用https 协议,所以服务端无法改动,只能在客户端排查问题。


一、问题

从错误信息得到大概意思就是在调用https请求时候校验证书异常。

说到这个问题,我自己搭建一个 https服务, 使用 jdk 自带 keytool 工具生成证书验证了一下:
要能正确的调用https 请求, 要满足
1.客户端要导入由服务端通过 keystore 导出的证书文件;
2.服务生成证书时候填写的CN 信息(域名) 和客户端访问的url的域名要一致(本地测试可以修改hosts文件配置域名)。

1.实际问题

但是!
第三方厂家 cas 服务端的https 证书就随便搞的,连CN信息都是随便写的不知道啥意思。
而且,虽是https 但还是通过ip 端口方式访问。 *哎, 这明显是应付入网安评嘛 *
让我如何是好!

二、大海捞针

1.星星之火

网上搜了很多。
只能绕过 https 证书校验去访问了。
看到一篇和我同样问题的文章:记一次单点登录https中证书无效的问题
他的方式是修改 CommonUtils 源码,然后重新编译打包替换原来的包,这种方式虽说能解决但是不通用,为啥尼,比如代码需要重新下载依赖,那还是每次都要重新替换,而且其他项目想校验证书用到了这个类,就难办了。

而且。他的答案漏了一行重要的代码,下文会提到。

2.通用方法

对于java 需要修改源码,我们一般会使用继承重写的方式。
但是对应本项目。
在这里插入图片描述
问题就出现在 CommonUtils类中 getResponseFromServer 这个方法。不能在这地方直接修改,否则只能像刚才那个网友搞了。找找到哪里用到这个地方。
往前找
在这里插入图片描述

AbstractCasProtocolUrlBasedTicketValidator.class-retrieveResponseFromServer 一看 final 修饰,没戏,再往前找。

在这里插入图片描述

AbstractUrlBasedTicketValidator.class - validate 该方法被final 修饰,重写不了。再往前

在这里插入图片描述
CasAuthenticationProvider.class - authenticateNow , 这个是 private 私有方法,重写不了。
再往前
在这里插入图片描述
CasAuthenticationProvider.class - authenticate ,哎CasAuthenticationProvider,好熟悉的眼神, 没错就是你了,在配置cas就见过你。再加上这个方法是 public,于是就重写这个方法,从这开刀。

啰嗦一句

建议新手多多学会使用 debuger, 你会发现越来越上瘾
在这里插入图片描述


解决

2.新建三个类

在这里插入图片描述
CasAuthenticationProviderExt

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package cn.com.liyx.learn.lyxsec.ext;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.*;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.*;
import org.springframework.util.Assert;public class CasAuthenticationProviderExt extends CasAuthenticationProvider {private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();private Cas20ProxyTicketValidatorExt ticketValidator;private ServiceProperties serviceProperties;public void afterPropertiesSet() {Assert.notNull(this.ticketValidator, "A ticketValidator must be set");}public Authentication authenticate(Authentication authentication) throws AuthenticationException {if (!this.supports(authentication.getClass())) {return null;} else if (authentication instanceof UsernamePasswordAuthenticationToken && !"_cas_stateful_".equals(authentication.getPrincipal().toString()) && !"_cas_stateless_".equals(authentication.getPrincipal().toString())) {return null;} else if (authentication instanceof CasAuthenticationToken) {if (getKey().hashCode() == ((CasAuthenticationToken) authentication).getKeyHash()) {return authentication;} else {throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProviderExt.incorrectKey", "The presented CasAuthenticationToken does not contain the expected key"));}} else if (authentication.getCredentials() != null && !"".equals(authentication.getCredentials())) {boolean stateless = false;if (authentication instanceof UsernamePasswordAuthenticationToken && "_cas_stateless_".equals(authentication.getPrincipal())) {stateless = true;}CasAuthenticationToken result = null;if (stateless) {result = getStatelessTicketCache().getByTicketId(authentication.getCredentials().toString());}if (result == null) {result = this.authenticateNow(authentication);result.setDetails(authentication.getDetails());}if (stateless) {getStatelessTicketCache().putTicketInCache(result);}return result;} else {throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProviderExt.noServiceTicket", "Failed to provide a CAS service ticket to validate"));}}private CasAuthenticationToken authenticateNow(Authentication authentication) throws AuthenticationException {try {Assertion assertion = this.ticketValidator.validate2(authentication.getCredentials().toString(), this.getServiceUrl(authentication));UserDetails userDetails = this.loadUserByAssertion(assertion);userDetailsChecker.check(userDetails);return new CasAuthenticationToken(getKey(), userDetails, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);} catch (TicketValidationException var4) {throw new BadCredentialsException(var4.getMessage(), var4);}}private String getServiceUrl(Authentication authentication) {String serviceUrl;if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {serviceUrl = ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();} else {if (this.serviceProperties == null) {throw new IllegalStateException("serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");}if (this.serviceProperties.getService() == null) {throw new IllegalStateException("serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");}serviceUrl = this.serviceProperties.getService();}if (logger.isDebugEnabled()) {logger.debug("serviceUrl = " + serviceUrl);}return serviceUrl;}public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {this.authoritiesMapper = authoritiesMapper;}public void setServiceProperties(ServiceProperties serviceProperties) {this.serviceProperties = serviceProperties;}protected Cas20ProxyTicketValidatorExt getTicketValidator() {return this.ticketValidator;}public void setTicketValidator(Cas20ProxyTicketValidatorExt ticketValidator) {this.ticketValidator = ticketValidator;}
}

Cas20ProxyTicketValidatorExt

package cn.com.liyx.learn.lyxsec.ext;import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;import java.net.MalformedURLException;
import java.net.URL;public class Cas20ProxyTicketValidatorExt extends Cas20ProxyTicketValidator {public Cas20ProxyTicketValidatorExt(String casServerUrlPrefix) {super(casServerUrlPrefix);}public final Assertion validate2(String ticket, String service) throws TicketValidationException {String validationUrl = this.constructValidationUrl(ticket, service);this.logger.debug("Constructing validation url: {}", validationUrl);try {this.logger.debug("Retrieving response from server.");
//            String serverResponse = this.retrieveResponseFromServer(new URL(validationUrl), ticket);String serverResponse = CommonUtilsExt.getResponseFromServer(new URL(validationUrl), this.getURLConnectionFactory(), this.getEncoding());if (serverResponse == null) {throw new TicketValidationException("The CAS server returned no response.");} else {this.logger.debug("Server response: {}", serverResponse);return this.parseResponseFromServer(serverResponse);}} catch (MalformedURLException var5) {throw new TicketValidationException(var5);}}
}

CommonUtilsExt

package cn.com.liyx.learn.lyxsec.ext;import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;public class CommonUtilsExt {private static final Logger LOGGER = LoggerFactory.getLogger(CommonUtils.class);static {disableSslVerification();}private static void disableSslVerification() {try {// Create a trust manager that does not validate certificate chainsTrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {public X509Certificate[] getAcceptedIssuers() {return null;}public void checkClientTrusted(X509Certificate[] certs, String authType) {}public void checkServerTrusted(X509Certificate[] certs, String authType) {}}};// Install the all-trusting trust managerSSLContext sc = SSLContext.getInstance("SSL");sc.init(null, trustAllCerts, new java.security.SecureRandom());HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());// Create all-trusting host name verifierHostnameVerifier allHostsValid = new HostnameVerifier() {public boolean verify(String hostname, SSLSession session) {return true;}};// Install the all-trusting host verifierHttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (KeyManagementException e) {e.printStackTrace();}}public static String getResponseFromServer(URL constructedUrl, HttpURLConnectionFactory factory, String encoding) {HttpURLConnection conn = null;InputStreamReader in = null;try {
//            conn = factory.buildHttpURLConnection(constructedUrl.openConnection());  // 原来的写法。conn = (HttpURLConnection) constructedUrl.openConnection();  // 新的写法, (注)本文要这样的写法if (CommonUtils.isEmpty(encoding)) {in = new InputStreamReader(conn.getInputStream());} else {in = new InputStreamReader(conn.getInputStream(), encoding);}StringBuilder builder = new StringBuilder(255);int byteRead;while ((byteRead = in.read()) != -1) {builder.append((char) byteRead);}String var7 = builder.toString();return var7;} catch (RuntimeException var13) {throw var13;} catch (SSLException var14) {LOGGER.error("SSL error getting response from host: {} : Error Message: {}", new Object[]{constructedUrl.getHost(), var14.getMessage(), var14});throw new RuntimeException(var14);} catch (IOException var15) {LOGGER.error("Error getting response from host: [{}] with path: [{}] and protocol: [{}] Error Message: {}", new Object[]{constructedUrl.getHost(), constructedUrl.getPath(), constructedUrl.getProtocol(), var15.getMessage(), var15});throw new RuntimeException(var15);} finally {CommonUtils.closeQuietly(in);if (conn != null) {conn.disconnect();}}}}

前面说那个网友漏掉重要一行, 就是下图这一行, 确实重要!!!
在这里插入图片描述

配置文件修改

在这里插入图片描述

注意就是方法返回类型 以及 属性类型也需要用新增的类名,不然会莫名报错。

完工。

相关内容

热门资讯

创新创业课程的心得体会和收获,...   我已经为餐饮培训了10天。在这里,我写下我的训练经历。来这里之前,我打算学几个技能,开店。然而,...
电商创业赚钱吗,总结电商创业感...   29岁,      农村出身,      丈夫来自工薪家庭。目前我也在找工作。      感叹!...
杭州创业贷,杭州最适合创业的城...   “建行的‘天目云贷’这么快就到位了。感谢区农业局的推荐,感谢建行的办事效率,解决了我们的燃眉之急...
创业路上的励志句子,创业路上的...   11月29日,格隆汇(600278。SH)公布a股限制性股票激励计划(草案),拟向激励对象授予不...
如何申请无息创业贷,青年创业无...   银行审批贷款慢,很多急需用钱的人不考虑一些小额免息贷款。然而,不是每个人都可以申请,今天边肖发言...
三姐妹创业的电视剧,奶茶三人组...   Yes娱乐12月20日综合报导      由江苏卫视和优酷视频联合播出的国内首档综艺节目《我想开...
网上咖啡店创业计划书,创业计划...   在美国,有一家独特的咖啡店。      它不招待普通人类顾客,但却是小动物的天堂。      在...
银川大学生创业补贴,大学生一次...   为加快银川市技能人才队伍建设,积极引导和鼓励大中专毕业生向一线技能岗位流动,近日,我市启动了20...
什么人适合开干洗店,在县城开干...   事实上,许多自己创业的人都是从单干开始的。如果他们想找一个志同道合的人做伴侣,也不是不可能。但是...
创业酵母谈运营,创业酵母经营管...   2019年3月21日,创业酵母CHO莫春娟受邀为小米集团组织部分享《如何做值得业务信赖的HRBP...