CAS 5.3.x 单点登录

目录

一:Cas server 搭建

二:证书配置

1:生成证书

2:导出数字证书

3:数字证书导入到JDK中

 4:配置tomcat 

三:添加数据库支持

1:注释掉静态用户名和密码

 2:添加新配置

三:客户端搭建 

 1:搭建个spring boot项目

2:配置Security

3:添加接口

4:未认证授权的服务


最近在网上搜索CAS 单点登录及第三方授权登录的功能,网上一搜一大堆,使用到处都是坑,那么多抄袭的,我也只能苦笑。。。

自己动手丰衣足食,所有还是把自己的正确功能记录一下,给大家做个参考

一:Cas server 搭建

CAS提供了模板 Overlay Template,方便使用 https://github.com/apereo/cas-overlay-template,当前版本是5.3.16,JDK 1.8+

IDEA导入下载的模板,暂时不修改任何熟悉,添加Tomcat

 启动Cas Server,打开http://localhost:8080/cas/login

输入用户名: casuser 密码:Mellon

静态的用户名和密码在application.properties里配置

 因为使用静态的用户和http连接,左侧有提示

黄色内容提示我们没有使用https,本地暂时使用的是http

蓝色内容提示我们使用了静态的用户登录,当然我们会在后面添加数据库支持

二:证书配置

1:生成证书

HTTPS需要证书的支持,所有我们借用JDK提供的工具keytool生成自己使用的证书

keytool -genkey -alias caskeystore -keypass cas123 -keyalg RSA -validity 36500 -keystore D:/Java/keys/cas.keystore

 名字与姓氏时为为具体路由地址,就是待会CAS认证服务器,sso.test.com,时间设置的比较长,36500单位是天

2:导出数字证书

 keytool -export -file D:/Java/keys/cas.crt -alias caskeystore -keystore D:/Java/keys/cas.keystore

 

3:数字证书导入到JDK中

keytool -import -keystore D:/Java/jdk1.8.0_161/jre/lib/security/cacerts -file D:/Java/keys/cas.crt -alias caskeystore -storepass changeit

因为我们没有这个域名,所有将本地的host文件修改下,添加以下

127.0.0.1       sso.test.com

 4:配置tomcat 

在server.xml中

    <Connector port="443" protocol="org.apache.coyote.http11.Http11AprProtocol" maxThreads="150" SSLEnabled="true" >
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="D:/Java/keys/cas.keystore" certificateKeystorePassword="cas123" type="RSA" />
        </SSLHostConfig>
    </Connector>

 访问 https://sso.test.com/cas/login

 

已经可以正常访问了。 

三:添加数据库支持

1:注释掉静态用户名和密码

#cas.authn.accept.users=casuser::Mellon

 2:添加新配置

#以下为本地的数据库配置信息
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/user?serverTimezone=UTC&allowMultiQueries=true
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].sql=select password from data_user where user_name = ?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
#指定过期字段,1为过期,若过期不可用(可选)
#cas.authn.jdbc.query[0].fieldExpired=expired
#为不可用字段段,1为不可用,需要修改密码(可选)
#cas.authn.jdbc.query[0].fieldDisabled=disabled

3:在pom.xml中添加配置

<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${cas.version}</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>

 

mysql版本根数据库版本一致就行 

建表语句

CREATE TABLE DATA_USER(
    ID VARCHAR(32)    COMMENT '编号' ,
    USER_NAME VARCHAR(60)    COMMENT '用户名' ,
    PASSWORD VARCHAR(60)    COMMENT '密码' ,
    CREATED_BY VARCHAR(32)    COMMENT '创建人' ,
    CREATED_TIME DATETIME   DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ,
    UPDATED_BY VARCHAR(32)    COMMENT '更新人' ,
    UPDATED_TIME DATETIME   DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间' 
)  COMMENT = '用户表';

三:客户端搭建 

 1:搭建个spring boot项目

 

以下是pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>client1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>client1</name>
    <description>client1</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 新增的引用 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-cas</artifactId>
            <version>5.5.2</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 新建application.yml

cas:
    server:
        host: https://sso.test.com/cas
        login: ${cas.server.host}/login
        logout: ${cas.server.host}/logout
app:
    server:
        host: http://localhost:${server.port}${server.servlet.context-path}
        login: /login/cas
        logout: /logout

server:
#    address: 127.0.0.1
    port: 9998
    servlet:
        context-path: /client1

# 为了方便调试可以打开
#logging.level.org.springframework.security: debug
#logging.level.web: debug

2:配置Security

添加SecurityConfig

@Configuration
@EnableWebSecurity //启用web权限
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法验证
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String CAS_AUTHENTICATION_PROVIDER_KEY = "casAuthenticationProviderKey";

    @Autowired
    private CasProperties casProperties;

    /**定义安全策略*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //禁用csrf保护机制
        http.csrf().disable();
        //禁用cors保护机制
        http.cors().disable();
        //禁用form表单登录
        http.formLogin().disable();
        //增加自定义过滤器

        http.authorizeRequests()//配置安全策略
//                .antMatchers("/**").permitAll()
                .antMatchers("/common/**").hasAnyRole("USER")
//                .anyRequest().authenticated()//其余的所有请求都需要验证
                .and()
                .logout()
                .permitAll();//定义logout不需要验证

        http.exceptionHandling()
                .authenticationEntryPoint(casAuthenticationEntryPoint())
                .and()
                .addFilter(casAuthenticationFilter())
                .addFilterBefore(casLogoutFilter(), LogoutFilter.class)
                .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth.authenticationProvider(casAuthenticationProvider());
    }

    /**
     * CAS认证入口点开始 =============================================================================
     */
    //即跳转至服务端的cas地址
    @Bean
    public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
        CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
        casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLogin());
        casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
        return casAuthenticationEntryPoint;
    }


    // 设置客户端service的属性
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        // 设置回调的service路径,此为主页路径
        // Cas Server认证成功后的跳转地址,这里要跳转到我们的Spring Security应用,
        // service 配置自身工程的根地址+/login/cas,表示是通过自身工程跳转到cas的登录页面,/login/cas 这个是security集成cas的固定写法
        serviceProperties.setService(casProperties.getAppServerHost() + casProperties.getAppServerLogin());
        // 对所有的未拥有ticket的访问均需要验证
        serviceProperties.setAuthenticateAllArtifacts(true);
        // sendRenew默认是false,true则意味着不允许单点登录,用户需要重新输入用户名密码以验证
        // 在安全级别要求比较高的情况下可以使用
        // serviceProperties.setSendRenew(false);
        return serviceProperties;
    }
    /**
     * CAS认证入口点结束 =============================================================================
     */

    /**
     * CAS认证过滤器开始 =============================================================================
     */
    @Bean
    public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
        CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
        casAuthenticationFilter.setAuthenticationManager(authenticationManager());
        casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppServerLogin());
        return casAuthenticationFilter;
    }

    /**
     * 创建CAS校验类
     * Notes:TicketValidator、AuthenticationUserDetailsService属性必须设置;
     * serviceProperties属性主要应用于ticketValidator用于去cas服务端检验ticket
     */
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider() {
        CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
        casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());
        casAuthenticationProvider.setServiceProperties(serviceProperties());
        casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
        casAuthenticationProvider.setKey(CAS_AUTHENTICATION_PROVIDER_KEY);
        return casAuthenticationProvider;
    }

    /**
     * 用户自定义的AuthenticationUserDetailsService
     */
    @Bean
    public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() {
        return new CustomUserDetailsService();
    }

    /**
     * 验证ticker,向cas服务器发送验证请求
     */
    @Bean
    public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
        // 配置上服务端的校验ticket地址
        return new Cas20ServiceTicketValidator(casProperties.getCasServerHost());
    }

    /**
     * CAS认证过滤器结束 =============================================================================
     */

    /**
     * CAS登出过滤器开始 =============================================================================
     */
    /**
     * 请求单点退出过滤器
     * @return
     */
    @Bean
    public LogoutFilter casLogoutFilter() {
        LogoutFilter logoutFilter =
                new LogoutFilter(casProperties.getCasServerLogout(), new SecurityContextLogoutHandler());
        logoutFilter.setFilterProcessesUrl(casProperties.getAppServerLogout());
        return logoutFilter;
    }

    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        return singleSignOutFilter;
    }
    /**
     * CAS登出过滤器结束 =============================================================================
     */
}

配置读取文件CasProperties

@Component
@Data
public class CasProperties {
    @Value("${cas.server.host}")
    private String casServerHost;
    @Value("${cas.server.login}")
    private String casServerLogin;
    @Value("${cas.server.logout}")
    private String casServerLogout;
    @Value("${app.server.host}")
    private String appServerHost;
    @Value("${app.server.login}")
    private String appServerLogin;
    @Value("${app.server.logout}")
    private String appServerLogout;
}

自定义授权CustomUserDetailsService

public class CustomUserDetailsService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
    /**
     * @param token The pre-authenticated authentication token
     * @return UserDetails for the given authentication token, never null.
     * @throws UsernameNotFoundException if no user details can be found for the given
     *                                   authentication token
     */
    @Override
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
        System.out.println("人员信息");
        List<GrantedAuthority> authorities = new ArrayList();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(token.getName(), "", authorities);
    }
}

3:添加接口

@RestController
@AllArgsConstructor
@RequestMapping("/common")
public class Common {

    @GetMapping( "/test")
    public String test() {
        return "客户端1";
    }
}

client2和client1配置基本相同,自己操作即可

启动clien项目,分别访问http://localhost:9998/client1/common/testhttp://localhost:9999/client2/common/test

发现以下错误

4:未认证授权的服务

出现以上问题是因为Cas Server未开启http协议

在服务端的WEB-INF/classess/application.properties添加以下内容

#设置安全为false
cas.tgc.secure=false
#开启识别json文件,默认false
cas.serviceRegistry.initFromJson=true

# 配置允许登出后跳转到指定页面
cas.logout.followServiceRedirects=true
# 跳转到指定页面需要的参数名为 service
cas.logout.redirectParameter=service

同样修改WEB-INF\classes\services\HTTPSandIMAPS-10000001.json,在serviceId的属性中添加http后重启服务端即可

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|http|imaps)://.*",
  "name" : "HTTPS and IMAPS",
  "id" : 10000001,
  "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
  "evaluationOrder" : 10000
}

现在重新打开http://localhost:9998/client1/common/test

 输入用户名和密码,登录

访问 http://localhost:9999/client2/common/test,直接显示内容

单点登录到这里就完成了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值