2020-10-14

SpringBoot整合CAS单点登录

CAS简介

CAS(Central Authentication Service) 是 Yale 大学发起的一个开源项目,是单点登录的一种现方式

官网地址:https://www.apereo.org/

Github地址:https://github.com/apereo/cas

分为CAS Server服务端和CAS Client客户端:

CAS Server:

  CAS Server 负责完成对用户的认证工作, CAS Server 需要独立部署,有不止一种 CAS Server 的实现, Yale CAS Server 和 ESUP CAS Server 都是很不错的选择。

CAS Server 会处理用户名 / 密码等凭证 (Credentials) ,它可能会到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户密码,对这种方式, CAS 均提供一种灵活但同一的接口 / 实现分离的方式, CAS 究竟是用何种认证方式,跟 CAS 协议是分离的,也就是,这个认证的实现细节可以自己定制和扩展.

CAS Client:

  CAS Client 负责部署在客户端(注意,我是指 Web 应用),原则上, CAS Client 的部署意味着,当有对本地 Web 应用的受保护资源的访问请求,并且需要对请求方进行身份认证, Web 应用不再接受任何的用户名密码等类似的 Credentials ,而是重定向到 CAS Server进行认证。

目前, CAS Client 支持(某些在完善中)非常多的客户端,包括 Java 、 .Net 、 ISAPI 、 Php 、 Perl 、 uPortal 、 Acegi 、 Ruby 、VBScript 等客户端,几乎可以这样说, CAS 协议能够适合任何语言编写的客户端应用。

 

HTTPS配置:

因为CAS要求所有的请求都要在一个安全的通道,所以要配置服务端的https。

1.生成服务器的密匙文件 wjx.keystore

keytool -genkey -alias wjx_cas -keyalg RSA -keysize 2048 -validity 36500 -keystore C:/opt/keys/wjx_cas.keystore

参数说明:

  -genkey 生成密钥

  -keyalg 指定密钥算法,这时指定RSA,

  -keysize 指定密钥长度,默认是1024位,这里指定2048,长一点,比较难破解,

  -validity 指定证书有效期,这里指定36500天.

  -alias 指定别名,这里是 wjx_cas

  -keystore 指定密钥库存储位置,这里存在  C:/opt/keys/目录下

注意:您的名字与姓氏 www.sso.com 是CAS服务器使用的域名,不是随便乱定的,其他的随意.

 

在cmd中找到当前目录,输入命令

输入结果:

 

2.导出证书

keytool -export -alias wjx_cas -storepass 123456 -file C:/opt/keys/wjx_cas.cer -keystore C:/opt/keys/wjx_cas.keystore

参数说明:

  -alias指定别名为 wjx_cas;

  -storepass指定私钥为123456;

  -file指定导出证书的文件名为 wjx_cas.cer;

  -keystore指定之前生成的密钥文件的文件名。

注意:-alias和-storepass必须为生成wolfcode .keystore密钥文件时所指定的别名和密码,否则证书导出失败.

当前目录下面的文件。

3.导入证书导入到jdk信任库

查看当前JDK版本,必须是 JDK.1.8 以上

找到当前的jdk安装的目录下面,C:/Program Files/Java/jdk1.8.0_191/jre/lib/security/

执行下面命令:

keytool -import -alias wolfcode -keystore C:/"Program Files"/Java/jdk1.8.0_191/jre/lib/security/cacerts -file C:/opt/keys/wjx_cas.cer -trustcacerts

 

注意:

1.原来的 $JAVA_HOME/jre/lib/security/cacerts 文件要先删掉,否则会报出java.io.IOException: Keystore was tampered with, or           password was incorrect错误.

2.如果路径有空格或特殊字符,像我上面一样加上引号.

正确截图:

 

使用cas-overlay-template搭建cas服务器

什么是maven的overlay?

        overlay可以把多个项目war合并成为一个项目,并且如果项目存在同名文件,那么主项目中的文件将覆盖掉其他项目的同名文件。

apereo提供了一个基于层结构的框架,可以帮助开发者快速引入cas server的代码,然后实现自由配置或代码覆盖,打包方式也非常简单。

Github地址: https://github.com/apereo/cas-overlay-template

使用步骤:

1.下载或者克隆cas-overlay-template(version:5.2)项目到本地,用Intellji Idea打开项目,下载依赖时间较长,需耐心等待,建议使用国内的镜像.

有个依赖下载很慢或者下载不了cas-server-webapp-tomcat,建议直接下载之后放到本地仓库对应的目录会快点.

http://mvnrepository.com/artifact/org.apereo.cas/cas-server-webapp-tomcat

环境要求:JDB1.8+

打开项目之后。

2.把pom.xml中配置的仓库注释/删除,因为仓库在国外,所以下载速度会比较慢,建议换成国内的镜像.

替换为

 <repositories>
        <repository>
            <releases>
                <enabled>true</enabled>
            </releases>
            <id>maven2-release</id>
            <url>http://uk.maven.org/maven2/</url>
        </repository>
 </repositories>
 
 
//如果在setting.xml文件中已经配置了阿里镜像也是可以的.

3.在项目中添加src/main/java和src/main/resources目录,并将src/main/java设置为代码文件根目录,将src/main/resources设置为资源文件根目录。

4.将overlays目录下的WEB-INF/classes/目录中的application.properties文件复制到src/main/resources中

5.将 证书 C:/opt/keys/wjx_cas.keystore  拷贝到resources 目录

6.修改application.properties文件.

7.打开命令行,进入项目所在目录,运行内置的命令: build.cmd run  ,执行时间较长,请耐心等待

CAS is configured to accept a static list of credentials for authentication. While this is generally useful for demo purposes, it is STRONGLY recommended that

这个只是警告,不是错误,意思是现在使用的是静态的验证,不够安全.

没关系,我们后续会换成数据库的验证.

这个下面还有一个,出现这个信息,表示CAS服务端已经启动了。

8.配置host

www.sso.com 这个域名,为当前CAS的服务端。必须和之前 生成证书的 名字与姓氏 一致。

9.访问CAS服务器

输入地址:https://www.sso.com:8443/cas/login

不用管,那是因为我们的证书是通过JDK生成的,不是认证机构发布的。

第一次进去会有点慢。跳转完成之后界面。

输入,用户密码:wjx/123456

使用SpringBoot搭建cas客户端

导入maven依赖

<!--SpringBoot整合cas客户端-->
<dependency>
    <groupId>net.unicon.cas</groupId>
    <artifactId>cas-client-autoconfig-support</artifactId>
    <version>1.5.0-GA</version>
</dependency>
项目结构:聚会项目,里面有三个服务,公共模块、会员服务、订单服务、支付服务

sso-common模块

CASUtil.java

package com.wjx.config;
 
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
 
import javax.servlet.http.HttpServletRequest;
 
/**
 * cas client常用工具类
 *
 * @author wjx
 */
public class CASUtil {
 
    /**
     * 从cas中获取用户名
     *
     * @param request
     * @return
     */
    public static String getAccountNameFromCas(HttpServletRequest request) {
        Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
       if(assertion!= null){
           AttributePrincipal principal = assertion.getPrincipal();
           return principal.getName();
       }else return null;
 
    }
}

sso-member模块

FilterConfig.java

我发现不加 SingleSignOutFilter 类过滤,单点退出之后,依然可以访问,加上这个就可以了,原因不清楚

package com.wjx.config;
 
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/3/5 16:02
 */
@Configuration
public class FilterConfig {
 
    @Value("${cas.server-url-prefix}")
    private String CAS_URL;
 
    @Value("${cas.client-host-url}")
    private String APP_URL;
 
    /**
     * 单点登录退出
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean singleSignOutFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new SingleSignOutFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.addInitParameter("casServerUrlPrefix", CAS_URL);
        registrationBean.setName("CAS Single Sign Out Filter");
        registrationBean.setOrder(1);
        return registrationBean;
    }
 
 
    /**
     * 获取当前用户
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean registrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new LocalUserInfoFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("localUserInfoFilter");
        registrationBean.setOrder(2);
        return registrationBean;
    }
}

LocalUserInfoFilter.java

package com.wjx.config;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
 
import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
/**
 * 本地用户信息过滤器
 *
 * @author wjx
 * @Date 2019/3/5 16:02
 */
public class LocalUserInfoFilter implements Filter {
 
    Logger logger = LoggerFactory.getLogger(LocalUserInfoFilter.class);
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request_ = (HttpServletRequest) request;
        String loginName = CASUtil.getAccountNameFromCas(request_);
        if (!StringUtils.isEmpty(loginName)) {
            logger.info("访问者 :" + loginName);
            request_.getSession().setAttribute("loginName", loginName);
        }
 
        chain.doFilter(request, response);
    }
 
    @Override
    public void destroy() {
 
    }
}
 
 

MemberController.java

package com.wjx.web;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/3/5 12:03
 */
@RestController
public class MemberController {
 
    @Value("${server.port}")
    private String serverPort;
 
    @GetMapping("/")
    public String hello() {
        return "我是会员服务:" + serverPort;
    }
 
}

AppMember.java

package com.wjx;
 
import net.unicon.cas.client.configuration.EnableCasClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
/**
 * @Description:
 * @Auther: wjx
 * @Date: 2019/3/5 11:29
 */
@SpringBootApplication
@EnableCasClient
public class AppMember{
 
    public static void main(String[] args) {
        SpringApplication.run(MemberApp.class);
    }
}
要想使客户端生效,要加上注解@EnableCasClient

application.properties配置

# 项目端口
server.port=8090
# 填CAS服务器的前缀
cas.server-url-prefix=https://www.sso.com:8443/cas
# 填CAS服务器的登录地址
cas.server-login-url=https://www.sso.com:8443/cas/login
# 填客户端的访问前缀 www.member.com是在host文件中配置的映射,映射到127.0.0.1
cas.client-host-url=http://www.member.com:8090
 
cas.validation-type=CAS

启动项目会员项目。输入地址访问:http://www.member.com:8090

可以发现,客户端已经跳转到服务端的登录界面。但是出现了未认证授权的服务,这个需要对服务代码做一些修改。

解决方法:

解决办法: 服务端开启http,默认只开始https和imaps。

操作步骤(1):将overlays目录下面的service拷贝到src下面进行修改,项目启动sr目录代码会覆盖overlays代码

操作步骤(2): 修改services\HTTPSandIMAPS-10000001.json文件

"serviceId" : "^(https|imaps)://.*"

改为==>

"serviceId" : "^(https|http|imaps)://.*",

操作步骤(3): 在application.properties文件中添加:

cas.tgc.secure=false

cas.serviceRegistry.initFromJson=true

重启cas服务端。

解决了。

其他几个服务按照member配置,注意修改端口号就可以了。

 

配置cas数据库

步骤一:在服务端cas-overlay-template-5.2的pom添加

<!--新增支持jdbc验证-->
<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${cas.version}</version>
</dependency>
<!--使用mysql驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.21</version>
</dependency>

步骤二:导入数据库sql脚本

/*
SQLyog Ultimate v12.09 (64 bit)
MySQL - 5.7.21 : Database - cas
*********************************************************************
*/
 
 
/*!40101 SET NAMES utf8 */;
 
/*!40101 SET SQL_MODE=''*/;
 
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`cas` /*!40100 DEFAULT CHARACTER SET utf8 */;
 
USE `cas`;
 
/*Table structure for table `sys_user` */
 
DROP TABLE IF EXISTS `sys_user`;
 
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `expired` int(11) DEFAULT NULL,
  `disabled` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
 
/*Data for the table `sys_user` */
 
insert  into `sys_user`(`id`,`username`,`password`,`expired`,`disabled`) values (1,'test','202cb962ac59075b964b07152d234b70',1,0),(2,'admin','202cb962ac59075b964b07152d234b70',0,0),(3,'root','202cb962ac59075b964b07152d234b70',0,0),(4,'wjx','202cb962ac59075b964b07152d234b70',0,0),(5,'zhangsan','202cb962ac59075b964b07152d234b70',0,1);
 
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

密码都是:123

步骤三:对密码进行MD5加密,application.properties将cas.authn.accept.users注释,配置文件添加

#Encode Database Authentication 开始
#加密次数
cas.authn.jdbc.encode[0].numberOfIterations=2
#该列名的值可替代上面的值,但对密码加密时必须取该值进行处理
#cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
# 盐值固定列
cas.authn.jdbc.encode[0].saltFieldName=username
#静态盐值
cas.authn.jdbc.encode[0].staticSalt=.
cas.authn.jdbc.encode[0].sql=select * from sys_user_encode where username=?
#对处理盐值后的算法
cas.authn.jdbc.encode[0].algorithmName=MD5
cas.authn.jdbc.encode[0].passwordFieldName=password
cas.authn.jdbc.encode[0].expiredFieldName=expired
cas.authn.jdbc.encode[0].disabledFieldName=disabled
cas.authn.jdbc.encode[0].url=jdbc:hsqldb:mem:cas-hsql-database
cas.authn.jdbc.encode[0].dialect=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8
cas.authn.jdbc.encode[0].user=root
cas.authn.jdbc.encode[0].password=admin
cas.authn.jdbc.encode[0].driverClass=com.mysql.jdbc.Driver
 
#Encode Database Authentication 结束

重启服务器。因为要连接数据库,第一次会慢一些。

 

发现没有了警告,输入数据库的用户名:admin/123

会员服务登录成功之后,订单服务就不需要登录了。

配置单点退出

输入地址:https://www.sso.com:8443/cas/logout

再去访问:http://www.member.com:8090/ 地址

完成,测试订单服务也是跳转到登录服务界面。

由于跳转到注销成功界面,不好,我们需要跳转到指定路径

解决方法:

服务端的application.properties添加

###允许退出之后跳转到登录页面,并且可以携带service参数到指定页面
cas.logout.followServiceRedirects=true

重启服务器。

输入地址:https://www.sso.com:8443/cas/logout?service=http://www.member.com:8090/

界面变成下面:发现把登录地址自动带在后边。

至此配置完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值