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();}}}}

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

配置文件修改

在这里插入图片描述

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

完工。

相关内容

热门资讯

小工厂管理员工的最好方法,小工...   *此文留给有缘人,请收藏转发,谢谢!      _1.从金融市场看公司管理,借鉴龙头企业的管理模...
老板是怎么创业的,老板的创业历...   普通人创业一定不要做你不擅长的事情。别人说的再好,一开始也轮不到你受益。即使有好的东西,也是有风...
售货店创业模式,无人售货店创新...   继续更新。谢谢你的等待。      很多粉丝在后台留言,说我主页上的文章他们都看了,或者2-3遍...
就业创业辩论赛发言,毕业后先创...   每年到了毕业季,求职者心里都会有冲突。我应该选择自己喜欢的工作还是申请高薪职位?        ...
女人创业做什么最吃香,创业需要...   #一      理财,对于很多人来说,总认为现在没钱,所以没必要理财。有句话说你不理财,钱不理你...
芋见甜品官网,芋姐带你餐饮创业...         没有芋头怎么说芋头?有点太q弹了,但是家里没有芋头和红薯,我想吃甜点。只要水温足够热...
天谕手游机缘巧合任务,天谕手游...   #电商、互联网资讯            1、2019天猫双11,商家官方群、淘宝直播等工具赋能...
创业板新股如何申购,创业板申购...   根据初步安排,本周(9月27日-9月30日),a股市场将迎来8只新股认购。上述8只新股中,上海主...
河北社交电商app平台,唐山社...   买水果,送快递,印刷材料.如今,对于唐山市万达小区等小区的业主来说,生活琐事已经成为下楼散步时简...
阿里收购饿了么赚钱吗,饿了么资...         机会总是留给有准备的人。      随着互联网的发展,人们的生活方式发生了巨大的改变...