美文网首页
pandaAnthony:spring boot cas 服务端

pandaAnthony:spring boot cas 服务端

作者: pandaAnthony | 来源:发表于2018-09-11 00:10 被阅读0次

这个系列有3篇文章:

  1. spring boot cas 服务端和spring security客户端搭建(一):主要介绍怎么用spring boot cas搭建https的服务端
  2. spring boot cas 服务端和spring security客户端搭建(二):主要介绍怎么用怎么结合数据库用cas提供的动态加密方式登录及相关配置
  3. spring boot cas 服务端和spring security客户端搭建(三):主要介绍怎么结合spring security客户端进行整合及http方式登录配置

终于腾出时间来写完这个系列了。
接着上篇spring boot cas 服务端和spring security客户端搭建(二)继续介绍cas与spring security客户端的整合。官网的资料只在github上找到了客户端的代码https://github.com/apereo/java-cas-client#spring-security-integration,而且还不是spring boot版本的:

image.png image.png
由于学习spring boot不久,还要花更多时间研究其他功能,就没花时间去把这些xml配置转成spring boot的配置了;参考https://blog.csdn.net/cl_andywin/article/details/53998986,搭建了基于spring boot的spring security cas客户端。

引入相关的依赖包

在对应工程的pom文件中引入spring-security-cas包(其他相关spring boot 的包就不在这里描述了,这里的包的版本和spring boot parent版本对应)
spring boot版本,项目中用的是2.0.3.RELEASE:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath/>
</parent>

spring boot security和 security cas包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
</dependency>

配置文件和配置类

CasProperties,cas服务端和客户端相关的配置信息(这里用到了lombok.Data来生成get,set方法):

@Data
@Component
public class CasProperties {
    
    @Value("${security.cas.server.host}")
    private String casServerUrl;
 
    @Value("${security.cas.server.login}")
    private String casServerLoginUrl;
 
    @Value("${security.cas.server.logout}")
    private String casServerLogoutUrl;
 
    @Value("${security.cas.service.host}")
    private String appServerUrl;
 
    @Value("${security.cas.service.login}")
    private String appLoginUrl;
 
    @Value("${security.cas.service.logout}")
    private String appLogoutUrl;

}

对应的配置信息(这时cas服务用的是http形式的了,因为环境限制不能用域名的,而且http的省事,在文章的后面会介绍怎么使用http),配置的具体含义这里就不描述了:

security:
  cas:
    server:
      host: http://xxx.xxx.xxx.xxx:8443/cas
      login: ${security.cas.server.host}/login
      logout: ${security.cas.server.host}/logout?service=${security.cas.service.host}
    service:
      host: http://localhost:8764
      login: /login
      logout: /logout

WebSecurityConfig配置类,最核心的配置(上面cas相关地址的配置不一定要那样实现,可能有更灵活的办法;下面登出的相关配置没有起到想象的作用,一个应用登出了,其他应用不会一起登出;后续找到办法了会继续补充):

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CasProperties casProperties;

    /**
     * configure(AuthenticationManagerBuilder): 身份验证配置,用于注入自定义身份验证Bean和密码校验规则
     * 
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth.authenticationProvider(casAuthenticationProvider());
    }

    /**
     * configure(WebSecurity): Web层面的配置,一般用来配置无需安全检查的路径
     * 
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**", "/templates/**");
    }

    /**
     * configure(HttpSecurity): Request层面的配置,对应XML Configuration中的<http>元素
     * 
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()// 配置安全策略
                .antMatchers("/login/cas").permitAll()
                .and().authorizeRequests().anyRequest().authenticated()
                .and().logout().permitAll()// 定义logout不需要验证
                .and().formLogin().defaultSuccessUrl("/index"); // 登录成功之后的跳转

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

        http.csrf().disable();

        // 关闭spring security默认的frame访问限制
        http.headers().frameOptions().sameOrigin();
    }

    @Bean
    public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
        CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
        casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());
        casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
        return casAuthenticationEntryPoint;
    }

    /**
     * 指定service相关信息
     */
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());
        // serviceProperties.setAuthenticateAllArtifacts(true);
        serviceProperties.setSendRenew(false);
        return serviceProperties;
    }

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

    /**
     * cas 认证 Provider
     */
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider() {
        CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
        casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());
        // 这里只是接口类型,实现的接口不一样,都可以的。
        casAuthenticationProvider.setServiceProperties(serviceProperties());
        // casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
        casAuthenticationProvider.setTicketValidator(cas30ServiceTicketValidator());
        casAuthenticationProvider.setKey("casAuthenticationProviderKey");
        return casAuthenticationProvider;
    }

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

    @Bean
    public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
        return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl());
    }

    @Bean
    public Cas30ServiceTicketValidator cas30ServiceTicketValidator() {
        return new Cas30ServiceTicketValidator(casProperties.getCasServerUrl());
    }

    /**
     * 单点登出过滤器
     */
    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        return singleSignOutFilter;
    }

    /**
     * 请求单点退出过滤器
     */
    @Bean
    public LogoutFilter casLogoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(),
                new SecurityContextLogoutHandler());
        logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl());
        return logoutFilter;
    }

    public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener() {
        return new SingleSignOutHttpSessionListener();
    }

    @Bean
    public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration() {
        ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<EventListener>();
        registrationBean.setListener(new SingleSignOutHttpSessionListener());
        registrationBean.setOrder(1);
        return registrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListenerBean() {
        ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listenerRegistrationBean = new ServletListenerRegistrationBean<>();
        listenerRegistrationBean.setEnabled(true);
        listenerRegistrationBean.setListener(singleSignOutHttpSessionListener());
        listenerRegistrationBean.setOrder(3);
        System.out.println("================================singleListener执行");
        return listenerRegistrationBean;
    }

}

其中需要自己去实现CustomUserDetailsService的用户认证逻辑:

@Service
public class CustomUserDetailsService //实现AuthenticationUserDetailsService,实现loadUserDetails方法
    implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {           
    @Override
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
        // 结合具体的逻辑去实现用户认证,并返回继承UserDetails的用户对象;
        return XXX;
    }

}

其中UserDetails是个接口:


image.png

到这里,spring boot下spring security整合cas的客户端基本就构建完成了。

http方式的CAS服务端

在第一篇文章(https://www.jianshu.com/p/0b3375b10860)介绍的cas-overlay-template下的配置文件application.properties中修改:

# 去掉https验证
cas.tgc.secure=false
cas.warn.cookie.secure=false
server.ssl.enabled=false

其中cas.tgc.secure和cas.warn.cookie.secure是新增的,server.ssl.enabled默认的是true:


image.png

默认情况下,CAS是只支持https请求的;还有个地方需要修改。因为是spring boot结构,可以直接覆盖原有文件,这里就不在额外的加配置了,直接在serveices文件夹下修改HTTSPandIMAPS-10000001.json:


image.png
image.png

这个文件可以在打包后的target下找到:


image.png

在https的边上多加个‘|http’就可以了:


image.png

这样CAS就能支持http方式了。

相关文章

网友评论

      本文标题:pandaAnthony:spring boot cas 服务端

      本文链接:https://www.haomeiwen.com/subject/hioegftx.html