SpringBoot2+SpringSecurity+CAS 安全认证整合项目

1. 写在前面

  如何你能看到这边文章,那我觉得我也不需要多废话,文章里面的东西你自然可以看懂。此篇文章只介绍整合过程,不介绍原理,适合懂原理想快速搭建环境的人儿们。
  介绍一下开发环境,CAS服务端使用的是CAS5.3;SpringBoot使用的是2.0.1
注:CAS服务端的安装如果不会,可以看我其他文章 docker快速搭建CAS
  先晒一下项目目录:
在这里插入图片描述

2. pom.xml

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

    <dependencies>
        <!--        spring security cas-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-cas</artifactId>
        </dependency>

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

        <!--        spring web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>

3. application.yml

server:
  port: 7200

#cas配置
cas:
  #秘钥
  key: 123
  server:
    host:
      #cas服务端地址 这是我的cas服务端地址 需要修改成你们的cas服务端地址
      url: http://192.168.16.122:8080/cas
      #cas服务端登录地址
      login_url: ${cas.server.host.url}/login
      #cas服务端登出地址 service参数后面跟就是需要跳转的页面/接口 这里指定的是cas客户端登录接口
      logout_url: ${cas.server.host.url}/logout?service=${cas.service.host.url}${cas.service.host.login_url}
  service:
    host:
      #cas客户端地址
      url: http://localhost:${server.port}
      #cas客户端地址登录地址
      login_url: /login
      #cas客户端地址登出地址
      logout_url: /logout

4. 创建配置类,映射yml中的变量


/**
 * @author : LCheng
 * @date : 2020-11-20 14:21
 * description : cas配置项
 */
@Data
@Component
public class CasProperties {

    /**
     * 秘钥
     */
    @Value("${cas.key}")
    private String casKey;

    /**
     * cas服务端地址
     */
    @Value("${cas.server.host.url}")
    private String casServerUrl;

    /**
     * cas服务端登录地址
     */
    @Value("${cas.server.host.login_url}")
    private String casServerLoginUrl;

    /**
     * cas服务端登出地址 并回跳到制定页面
     */
    @Value("${cas.server.host.logout_url}")
    private String casServerLogoutUrl;

    /**
     * cas客户端地址
     */
    @Value("${cas.service.host.url}")
    private String casServiceUrl;

    /**
     * cas客户端地址登录地址
     */
    @Value("${cas.service.host.login_url}")
    private String casServiceLoginUrl;

    /**
     * cas客户端地址登出地址
     */
    @Value("${cas.service.host.logout_url}")
    private String casServiceLogoutUrl;
}

5. 创建CasUserDetailService

/**
 * 用户登录操作
 *
 * @author lCheng
 */
@Component
@Slf4j
public class CasUserDetailService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {

    @Override
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken casAssertionAuthenticationToken) throws UsernameNotFoundException {
        log.info("登陆用户名: " + casAssertionAuthenticationToken.getName());
        //由于登录交于cas服务端管理,如果进入这里代表登录成功,可不做任何操作,也可以根据自身系统业务开发相关功能代码
        return new User(casAssertionAuthenticationToken.getName(), "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }
}

6. 创建cas 配置类


/**
 * @author : LCheng
 * @date : 2020-11-20 18:06
 * description : cas 配置中心
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CasSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CasProperties casProperties;

    @Autowired
    private AuthenticationUserDetailsService casUserDetailService;

    /**
     * 设置AuthenticationProvider
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(casAuthenticationProvider());
    }

    /**
     * security安全策略
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config = http.authorizeRequests()
                // 放行OPTIONS请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll();

        //剩下的所有请求都需要验证
        config.anyRequest().authenticated();

        http
                .logout()
                .permitAll()//放行logout
                .and()
                .formLogin();//表单登录

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

        http.csrf().disable(); //禁用CSRF
    }

    /**
     * cas认证入口
     */
    @Bean
    public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
        CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
        //设置cas 服务端登录url
        casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());
        //设置cas 客户端信息
        casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
        return casAuthenticationEntryPoint;
    }

    /**
     * cas客户端信息
     */
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        //设置cas客户端登录完整的url
        serviceProperties.setService(casProperties.getCasServiceUrl() + casProperties.getCasServiceLoginUrl());
        serviceProperties.setAuthenticateAllArtifacts(true);
        return serviceProperties;
    }

    /**
     * cas认证过滤器
     * casAuthenticationFilter.setFilterProcessesUrl 必须要设置完整路径 不然会无限重定向
     */
    @Bean
    public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
        CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
        casAuthenticationFilter.setAuthenticationManager(authenticationManager());
        casAuthenticationFilter.setFilterProcessesUrl(casProperties.getCasServiceUrl() + casProperties.getCasServiceLoginUrl());
        casAuthenticationFilter.setServiceProperties(serviceProperties());
        return casAuthenticationFilter;
    }

    /**
     * cas认证Provider
     */
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider() {
        CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
        //设置UserDetailService 登录成功后 会进入loadUserDetails方法中
        casAuthenticationProvider.setAuthenticationUserDetailsService(casUserDetailService);
        //设置cas 客户端信息
        casAuthenticationProvider.setServiceProperties(serviceProperties());
        //设置cas 票证验证器
        casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
        //设置cas 秘钥
        casAuthenticationProvider.setKey(casProperties.getCasKey());
        return casAuthenticationProvider;
    }

    /**
     * cas 票证验证器
     */
    @Bean
    public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
        return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl());
    }

    /**
     * 单点注销过滤器
     * 用于接收cas服务端的注销请求
     */
    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        return singleSignOutFilter;
    }

    /**
     * 单点退出过滤器
     * 用于跳转到cas服务端
     */
    @Bean
    public LogoutFilter logoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(), new SecurityContextLogoutHandler());
        logoutFilter.setFilterProcessesUrl(casProperties.getCasServiceLogoutUrl());
        return logoutFilter;
    }
}

7.最后一步,配置一个测试控制器


/**
 * @author : LCheng
 * @date : 2020-11-26 17:44
 * description : 测试控制器
 */
@Slf4j
@RestController
public class TestController {

    @SneakyThrows
    @GetMapping(value = "/login")
    public String login(HttpServletRequest request, HttpServletResponse response) {
        //只是为了跳转到cas服务端的登录页面  登录成功后会跳回此页
        //可根据自身系统进行代码增强
        return "登录成功了";
    }

    @GetMapping(value = "/logout")
    public void logout() {
        //只是为了跳转到cas服务端的登出页面
        //可根据自身系统进行代码增强
    }

    @GetMapping(value = "/hello")
    public String hello() {
    	//登录成功后,访问此接口,如果正常返回hello word,表示已授权成功
        return "hello word";
    }
}

8.最最后一步,实践检验真理的最后一步

  下面的git图片展示了整个测试过程:

  1. 访问http://localhost:7200/,由于没有授权,会跳到cas服务端登录页
  2. cas服务端登录页进行登录操作,登录成功后跳转回http://localhost:7200/login,提示登录成功
  3. 访问http://localhost:7200/logout,进行登出操作
  4. 成功登出后,由于我们配置了回调url,会再次引导跳转回http://localhost:7200//login,而此时已经退出登录,系统会再次引导我们进入cas服务端登录页
    注:application.yml中配置了登出回调url
  5. 重复步骤2的操作,再次登录成功
  6. 访问http://localhost:7200/hello,由于已经登录成功,不会跳转到cas服务端登录页,而是正常返回 hello word。

在这里插入图片描述

结束语

至此所有整合过程已经完毕。
源码已上传至gitee 地址为:https://gitee.com/lonecheng/springboot-springsecurity-cas

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值