Spring Boot CAS 集成

mac2024-03-17  34

                   最近项目中需要单点登录到其他系统,单点登录的服务校验用的是CAS,所以在网上查找了很多资料,最后结合前辈们的精华,终于搞定了,但是为了不让大家过多的去寻找资料汇总,我这边写一篇从安装到集成的,希望大家看到这篇文章就可以对CAS有个大体上了解,写的不好请大家不要喷我谢谢。

先看一下具体操纵流程(我这边使用的是REST方式进行对接CAS的)因为我的项目是老项目,已经有登陆了所以就不用跳转到CAS的登陆页面了。

对接CAS的流程就是上图的如果看不懂图片流程,也没关系,具体操作起来慢慢的一步一步的操作也能明白。

第一步:安装CAS服务器

下载服务器文件

https://github.com/apereo/cas-overlay-template

我这里下载5.2版本的

下载ZIP包

解压开就是下图这样了

是不是一脸懵,我刚下下载下来看了一下结构也是一脸懵,是maven项目,但是 没有src目录,这个项目就是么有代码目录,我们只能通过配置文件进行修改。

我们刚刚下载下来的这个CAS 服务本身是不支持REST的,我们需要添加RETS的配置,让CAS支持REST。对了还有数据库连接支持也一起写上。

我们打开pom.xml

添加如果支持

<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-jdbc-drivers</artifactId> <version>${cas.version}</version> </dependency> <!--jdbc认证需要添加的,这个是cas的依赖包--> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-jdbc</artifactId> <version>${cas.version}</version> </dependency> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-rest</artifactId> <version>${cas.version}</version> </dependency> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-rest-tokens</artifactId> <version>${cas.version}</version> </dependency> </dependencies>

添加完成后就可以打包了。

liunx 执行

./build.sh package

windows 执行

build.cmd package

直到出现构建成功即可如图(构建过程可能出现多次错误,重试就可以了)

 

如果不想自己构建,我这边为大家构建好了,直接拿去用就可以地址如下

https://download.csdn.net/download/cdszdd8/11945308

打好的war包放到容器里让容器为他自己解压,解压后目录如图

我们需要修改的就是下图这个application.properties(这里的目录是容器里解压后的项目目录啊,别找错了呢)

打开application.properties 替换如下代码

## # CAS Server Context Configuration # server.context-path=/cas server.port=8443 server.ssl.key-store=file:/etc/cas/thekeystore server.ssl.key-store-password=changeit server.ssl.key-password=changeit # server.ssl.ciphers= # server.ssl.client-auth= # server.ssl.enabled= # server.ssl.key-alias= # server.ssl.key-store-provider= # server.ssl.key-store-type= # server.ssl.protocol= # server.ssl.trust-store= # server.ssl.trust-store-password= # server.ssl.trust-store-provider= # server.ssl.trust-store-type= server.max-http-header-size=2097152 server.use-forward-headers=true server.connection-timeout=20000 server.error.include-stacktrace=ALWAYS server.compression.enabled=true server.compression.mime-types=application/javascript,application/json,application/xml,text/html,text/xml,text/plain server.tomcat.max-http-post-size=2097152 server.tomcat.basedir=build/tomcat server.tomcat.accesslog.enabled=true server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms) server.tomcat.accesslog.suffix=.log server.tomcat.max-threads=10 server.tomcat.port-header=X-Forwarded-Port server.tomcat.protocol-header=X-Forwarded-Proto server.tomcat.protocol-header-https-value=https server.tomcat.remote-ip-header=X-FORWARDED-FOR server.tomcat.uri-encoding=UTF-8 spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true spring.http.encoding.force=true ## # CAS Cloud Bus Configuration # spring.cloud.bus.enabled=false # spring.cloud.bus.refresh.enabled=true # spring.cloud.bus.env.enabled=true # spring.cloud.bus.destination=CasCloudBus # spring.cloud.bus.ack.enabled=true endpoints.enabled=false endpoints.sensitive=true endpoints.restart.enabled=false endpoints.shutdown.enabled=false management.security.enabled=true management.security.roles=ACTUATOR,ADMIN management.security.sessions=if_required management.context-path=/status management.add-application-context-header=false security.basic.authorize-mode=role security.basic.enabled=false security.basic.path=/cas/status/** ## # CAS Web Application Session Configuration # server.session.timeout=300 server.session.cookie.http-only=true server.session.tracking-modes=COOKIE ## # CAS Thymeleaf View Configuration # spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.cache=true spring.thymeleaf.mode=HTML ## # CAS Log4j Configuration # # logging.config=file:/etc/cas/log4j2.xml server.context-parameters.isLog4jAutoInitializationDisabled=true ## # CAS AspectJ Configuration # spring.aop.auto=true spring.aop.proxy-target-class=true ## # CAS Authentication Credentials # #cas.authn.accept.users=casuser::Mellon cas.serviceRegistry.initFromJson=true cas.authn.jdbc.query[0].sql=SELECT * FROM access_user WHERE name = ? cas.authn.jdbc.query[0].healthQuery= cas.authn.jdbc.query[0].isolateInternalQueries=false cas.authn.jdbc.query[0].url=jdbc:mysql://xx.xx.xx.x:3306/xxxx?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false cas.authn.jdbc.query[0].failFast=true cas.authn.jdbc.query[0].isolationLevelName=ISOLATION_READ_COMMITTED cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect cas.authn.jdbc.query[0].leakThreshold=10 cas.authn.jdbc.query[0].propagationBehaviorName=PROPAGATION_REQUIRED cas.authn.jdbc.query[0].batchSize=1 #修改为自己的数据库用户密码 cas.authn.jdbc.query[0].user=xxxx cas.authn.jdbc.query[0].password=xxx cas.authn.jdbc.query[0].ddlAuto=create-drop cas.authn.jdbc.query[0].maxAgeDays=180 cas.authn.jdbc.query[0].autocommit=false cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver cas.authn.jdbc.query[0].idleTimeout=5000 cas.authn.jdbc.query[0].credentialCriteria= cas.authn.jdbc.query[0].name= cas.authn.jdbc.query[0].order=0 cas.authn.jdbc.query[0].dataSourceName= cas.authn.jdbc.query[0].dataSourceProxy=false #密码字段的信息 cas.authn.jdbc.query[0].fieldPassword=password cas.ticket.st.numberOfUses=50 cas.ticket.st.timeToKillInSeconds=120 cas.logout.followServiceRedirects=true #加密策略 默认NONE未加密 可支持MD5 、 SHA #cas.authn.jdbc.query[0].passwordEncoder.type=MD5

在这里我们还需要修改一个文件,就是cas默认只支持https我们这边修改他让他可以支持http

我们找到下图的文件(这里的目录是容器解压后的目录啊,别找错了啊)

打开后添加一个http如图就可以了。

启动容器。出现如下图片说明启动成功了。

访问一下

到此服务端安装成功界面上的错误,是因为我们没有配置https这里配置https 我就不配置了,如果需要用的话,去下载一个免费的证书配置到容器上就可以了。不要为了这个去捣鼓你的jdk,我这边按网上的弄结果我太菜了,把jdk搞坏了,所以这个东西不好弄成功,所以还是不要弄了。

spring boot 集成CAS

我们在application-xxx.xml 中添加配置

#http://xxxxxx/cas 是cas的容器 #https://xxxxx.com 这个是我们服务1的地址 cas: casServerLoginUrl: http://xxxxxx/cas/login serverName: https://xxxxx.com casServerUrlPrefix: http://xxxx/cas useSession: true redirectAfterValidation: true exceptionOnValidationFailure: true

 建一个配置文件工具类

@ConfigurationProperties(prefix = "cas") @Configuration public class CasClientProperties { /** * 单点登录需要访问的CAS SERVER URL入口 */ private String casServerLoginUrl; /** * 托管此应用的服务器名称 */ private String serverName; /** * 指定是否应将renew = true发送到CAS服务器 */ private boolean renew = false; /** * 指定是否应将gateway = true发送到CAS服务器 */ private boolean gateway = false; /** * cas服务器的开头 例如 http://localhost:8443/cas */ private String casServerUrlPrefix; /** * 是否将Assertion 存入到session中 * 如果不使用session(会话),tickets(票据)将每次请求时都需要tickets */ private boolean useSession = true; /** * 是否在票证验证后重定向到相同的URL,但在参数中没有票证 */ private boolean redirectAfterValidation = true; /** * 是否在tickets验证失败时抛出异常 */ private boolean exceptionOnValidationFailure = false; private String logOutUrl; private String restUrl; public boolean isEnable() { return enable; } public void setEnable(boolean enable) { this.enable = enable; } public String getCasServerLoginUrl() { return casServerLoginUrl; } public void setCasServerLoginUrl(String casServerLoginUrl) { this.casServerLoginUrl = casServerLoginUrl; } public String getServerName() { return serverName; } public void setServerName(String serverName) { this.serverName = serverName; } public boolean isRenew() { return renew; } public void setRenew(boolean renew) { this.renew = renew; } public boolean isGateway() { return gateway; } public void setGateway(boolean gateway) { this.gateway = gateway; } public String getCasServerUrlPrefix() { return casServerUrlPrefix; } public void setCasServerUrlPrefix(String casServerUrlPrefix) { this.casServerUrlPrefix = casServerUrlPrefix; } public boolean isUseSession() { return useSession; } public void setUseSession(boolean useSession) { this.useSession = useSession; } public boolean isRedirectAfterValidation() { return redirectAfterValidation; } public void setRedirectAfterValidation(boolean redirectAfterValidation) { this.redirectAfterValidation = redirectAfterValidation; } public boolean isExceptionOnValidationFailure() { return exceptionOnValidationFailure; } public void setExceptionOnValidationFailure(boolean exceptionOnValidationFailure) { this.exceptionOnValidationFailure = exceptionOnValidationFailure; } public String getLogOutUrl() { return logOutUrl; } public void setLogOutUrl(String logOutUrl) { this.logOutUrl = logOutUrl; } public String getRestUrl() { return restUrl; } public void setRestUrl(String restUrl) { this.restUrl = restUrl; } }

再写个cas 操作工具类

/** * Stone.Cai * 2019年10月18日15:56:03 * 添加 * sso登录登出服务 */ @Service public class SsoService { @Autowired private AccessUserRepository accessUserRepository; @Autowired private CasClientProperties casClientProperties; /** * Stone.Cai * 2019年10月28日16:36:21 * 添加 * 获取TGT * @param username * @param password * @return */ private BaseDto findTgT(String username, String password){ if(StringUtils.isBlank(username)||StringUtils.isBlank(password)){ return BaseDto.error("数据传输错误!"); } List<NameValuePair> params =new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("username",username)); params.add(new BasicNameValuePair("password",password)); Map<String,String> header=new HashMap<String,String>(); header.put("Content-Type","application/x-www-form-urlencoded"); String res= HttpClientUtil.doPost(casClientProperties.getCasServerUrlPrefix()+"/v1/tickets",params,header); if(StringUtils.isBlank(res)){ return BaseDto.error("用户信息错误!"); } Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*").matcher(res); if (matcher.matches()) { return BaseDto.success("操作成功!",matcher.group(1)); } return BaseDto.error("用户信息错误!"); } /** * Stone.Cai * 2019年10月28日16:44:23 * 添加 * 根据用户信息获取ST * @return */ private BaseDto findST(String ticketGrantingTicket,String moduleName){ if(StringUtils.isBlank(ticketGrantingTicket)){ return BaseDto.error("数据传输错误!"); } List<NameValuePair> params =new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("service",casClientProperties.getLogOutUrl()+"/"+moduleName)); Map<String,String> header=new HashMap<String,String>(); header.put("Content-Type","application/x-www-form-urlencoded"); String res= HttpClientUtil.doPost(casClientProperties.getCasServerUrlPrefix()+"/v1/tickets/"+ticketGrantingTicket,params,header); if(StringUtils.isBlank(res)){ return BaseDto.error("获取用户登录失败!"); } return BaseDto.success("操作成功",res); } /** * Stone.Cai * 2019年10月28日16:55:20 * 添加 * 获取ST是否成功 * @param serviceTicket * @param moduleName * @return */ public BaseDto verifyServiceTicket(String serviceTicket, String moduleName){ Map<String, String> params =new HashMap<>(); params.put("ticket",serviceTicket); params.put("service",casClientProperties.getLogOutUrl()+"/"+moduleName); Map<String,String> header=new HashMap<String,String>(); header.put("Content-Type","application/x-www-form-urlencoded"); String res= HttpClientUtil.getRequest(casClientProperties.getCasServerUrlPrefix()+"/p3/serviceValidate",params,"","UTF-8"); if(StringUtils.isBlank(res)){ return BaseDto.error("获取用户登录失败!"); } int begin = res.indexOf("<cas:user>"); if (begin < 0) return BaseDto.error("获取用户登录失败!"); int end = res.indexOf("</cas:user>"); String user= res.substring(begin + 10, end); AccessUser myuser= accessUserRepository.isNameRepeat(user); if(myuser==null){ return BaseDto.error("获取用户登录失败!"); } JSONObject obj=new JSONObject(); obj.put("userName",myuser.getName()); obj.put("pwd",myuser.getPassword()); String code=""; try { code=AesUtil.aesPKCS7PaddingEncrypt(obj.toJSONString()); } catch (Exception e) { e.printStackTrace(); } return BaseDto.success("操作成功", URLEncoder.encode(code)); } /** * Stone.Cai * 2019年10月28日17:13:30 * 添加 * login * @param userName * @param password * @return */ public BaseDto login(String userName,String password){ //获取tgt BaseDto dto= findTgT(userName,password); // if(dto.getCode()!=200){ // return dto; // } //获取st //dto= findST(dto.getDataList().toString(),""); return dto; } /** * Stone.Cai * 2019年10月30日13:56:59 * 添加 * 获取ST进行单点登录 * @param tgt * @return */ public BaseDto loginST(String tgt){ return findST(tgt,""); } /** * Stone.Cai * 2019年10月31日09:42:41 * 添加 * 删除TGT 退出 * @param tgt * @return */ public BaseDto deleteTicketGrantingTicket(String tgt){ if (StringUtils.isBlank(tgt)) return BaseDto.error("用户信息错误!"); HttpClientUtil.deleteRequest(casClientProperties.getCasServerUrlPrefix()+"/v1/tickets/"+tgt); return BaseDto.success("操作成功!"); }

这样就后太就可以进行cas 校验了

服务器1 用户在登陆成功后接着调用 findTgT 来获取TGT 并保存起来 ,如果要跳转到服务器2 需要用TGT 获取  findST 能拿到ST后就可以跳转到 http://server2?ticket=ST 这样 到服务器2后如果检测到ticket有值就使用 verifyServiceTicket 来校验就可以获取到用了。

好吧,就写到这里吧,如果有疑问就评论里给我留言吧,我尽量给大家解释,谢谢观看。

 

 

 

 

最新回复(0)