Spring Security整合CAS

本文详细介绍了如何在Spring Boot项目中集成CAS(Central Authentication Service)以处理前后端分离场景的用户登录和登出流程,包括配置CasAuthenticationEntryPoint、CasAuthenticationFilter和定制重定向策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CAS(中央认证服务)

从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。图1 是 CAS 最基本的协议过程:

CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性

在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的

另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档

以上摘自百度百科


数据库设计

建表主要以下几张表,不过也可以添加一些其他表,比如保存前端模块的表module,保存前端菜单的表menu,至于表中字段根据自己需求来设计,学习也是需要自己来参与的。


相关依赖

主要依赖版本:

springboot-2.3.0
spring-security-casspring-security-cas 5.2.2

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

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

  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
    <version>5.2.2.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
  </dependency>

  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
  </dependency>

  <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
  </dependency>

  <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.2</version>
  </dependency>

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

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
  </dependency>

  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
  </dependency>
</dependencies>

配置文件

对于CAS服务端搭建就省略了,可以去官网 下载,实际就是一个war包,放到tomcat的webapps下就可以了,具体安装流程可以自行Google,资源有很多。

server:
  servlet:
    context-path: "/test"

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

#客户端地址
app:
  server:
    host:
      url: http://${host:localhost}:8080/test
  login:
    url: /login
  logout:
    url: /logout

#cas认证中心地址
cas:
  server:
    host: http://localhost:8081/cas
    login_url: http://localhost:8081/cas/login
    logout_url: http://localhost:8081/cas/logout?service=http://${host:localhost}:8080/test/app/login
#logging:
#  level:
#    root: debug


相关配置类

声明一下主要展示spring security如何配置CAS,其他基本操作就不一一列举。

首先新建一个YmlProperties类来读取application.yml的相关配置

@Component
@Data
public class YmlProperties {

  @Value("${app.server.host.url}")
  private String appServerUrl;

  @Value("${app.login.url}")
  private String appLoginUrl;

  @Value("${app.logout.url}")
  private String appLogoutUrl;

  @Value("${cas.server.host}")
  private String casServerUrl;

  @Value("${cas.server.login_url}")
  private String casServerLoginUrl;

  @Value("${cas.server.logout_url}")
  private String casServerLogoutUrl;
}

新建UserDetail类封装我们需要的用户信息

注意如果没有相关需求,此类可以不建,直接使用spring security提供的org.springframework.security.core.userdetails.User类。

public class UserDetail extends User implements UserDetails, CredentialsContainer {

  private Long userId;

  public UserDetail(String username,String password, Collection<? extends GrantedAuthority> authorities){
    super(username,password,true,true,true,true,authorities);
  }

  public Long getUserId() {
    return userId;
  }

  public void setUserId(Long userId) {
    this.userId = userId;
  }
}

新建MyUserDetailsService类实现AuthenticationUserDetailsService接口

涉及的查询语句可以自己完成,一个是根据用户名查询用户信息,一个是根据用户id查询角色权限,根据上述建的中间表关联查询,都是一些基础的查询语句,这里就不贴出来了。

@Service
public class MyUserDetailsService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken>{

  @Autowired
  private UserService userService;

  @Autowired
  private PermissionMapper permissionMapper;


  @Override
  public UserDetails loadUserDetails(CasAssertionAuthenticationToken token)
      throws UsernameNotFoundException {
    User user = userService.findByName(token.getName());
    if (Objects.isNull(user)) {
      throw new RuntimeException("用户不存在");
    }
List<String> roleCodes = permissionMapper.getPermissionByUser(user.getId());
    if (CollectionUtils.isEmpty(roleCodes)) {
      throw new RuntimeException("账户[" + user.getUsername() + "]未绑定角色");
    }
    Set<GrantedAuthority> authorities = new HashSet<>();
    for (String roleCode : roleCodes) {
      authorities.add(new SimpleGrantedAuthority(roleCode));
    }

    UserDetail userDetail=new UserDetail(user.getUsername(),user.getPassword(),authorities);
    userDetail.setUserId(user.getId());
    return userDetail;
  }

}

新建SecurityConfiguration类实现WebSecurityConfigurerAdapter接口

如果想了解每个Bean的相关作用和spring security是如何和CAS交互的,参考spring官网说明 ,官网详细介绍了整个流程,一定要自己理解。

不管是spring security还是其他技术,个人建议还是首先去看官网给的文档,他可以解决大部分问题,而后再去Google。

其实以下这些配置就是根据官网一步一步配置的,官网给的是xml配置,可能有的小伙伴一看到就直接放弃不看去Google了,如今都用springboot谁还学习xml如何配置,不过如果静下心去看的话,完全可以根据xml配置自己转化成配置类。

@Slf4j
@EnableWebSecurity
//开启@Secured和@PreAuthorize
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Autowired
  private YmlProperties ymlProperties;

  @Autowired
  private MyUserDetailsService myUserDetailsService;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests() //配置安全策略
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated() 
        .and()
        .csrf().disable()
        .logout().permitAll() //logout不需要验证
        .and()
        .headers().frameOptions().disable()
        .and()
        .cors()
        .and().formLogin(); //使用form表单登录

    http.exceptionHandling().authenticationEntryPoint(myCasAuthenticationEntryPoint)
        .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());
  }

  /**
   * 主要配置的是ServiceProperties的service属性,它指定的是cas回调的地址
   */
  @Bean
  public ServiceProperties serviceProperties() {
    ServiceProperties serviceProperties = new ServiceProperties();
    serviceProperties.setService(ymlProperties.getAppServerUrl() + ymlProperties.getAppLoginUrl());
    serviceProperties.setSendRenew(false);
    serviceProperties.setAuthenticateAllArtifacts(true);
    return serviceProperties;
  }

  /**
   * 认证的入口,指定cas服务器的登录地址,指定ServiceProperties(主要是获取回调地址)
   */
  @Bean
  public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
    CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
    casAuthenticationEntryPoint
        .setLoginUrl(ymlProperties.getCasServerLoginUrl());
    casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
    return casAuthenticationEntryPoint;
  }


  @Bean
  public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
    CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
    casAuthenticationFilter.setServiceProperties(serviceProperties());    casAuthenticationFilter.setFilterProcessesUrl(ymlProperties.getAppLoginUrl());
    casAuthenticationFilter.setAuthenticationManager(authenticationManager());

    //指定登录成功后跳转页面,也可以使用SavedRequestAwareAuthenticationSuccessHandler
//    SavedRequestAwareAuthenticationSuccessHandler handler=new SavedRequestAwareAuthenticationSuccessHandler();
//    handler.setRedirectStrategy(new MyRedirectStrategy());
//    casAuthenticationFilter.setAuthenticationSuccessHandler(handler);
    casAuthenticationFilter.setAuthenticationSuccessHandler(
        new SimpleUrlAuthenticationSuccessHandler(
            ymlProperties.getAppServerUrl() + "/hello"));
    casAuthenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
    return casAuthenticationFilter;
  }

//  class MyRedirectStrategy extends DefaultRedirectStrategy {
//
//    @Override
//    public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
//        throws IOException {
//      String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
//      redirectUrl = response.encodeRedirectURL(redirectUrl);
//      log.info(redirectUrl);
//      if(redirectUrl.startsWith("http://")){
//        if(redirectUrl.contains("/app/login")){
//          redirectUrl="http://localhost:8080/test/index";
//        }
//      }
//      response.sendRedirect(redirectUrl);
//    }
//  }


  @Bean
  public CasAuthenticationProvider casAuthenticationProvider() {
    CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
    casAuthenticationProvider.setServiceProperties(serviceProperties());
    casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
    casAuthenticationProvider
        .setAuthenticationUserDetailsService(myUserDetailsService);
    casAuthenticationProvider.setKey("casAuthenticationProviderKey");
    return casAuthenticationProvider;
  }


  /**
   * 验证ticker,向cas服务器发送验证请求
   */
  @Bean
  public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
    //转Https请求
//    HttpURLConnectionFactory httpURLConnectionFactory=new HttpURLConnectionFactory() {
//      @Override
//      public HttpURLConnection buildHttpURLConnection(URLConnection urlConnection) {
//        SSLContext sslContext=null;
//        try {
//          sslContext = SSLContext.getInstance("SSL");
//          sslContext.init(new KeyManager[]{},new TrustManager[]{new X509TrustManager() {
//            @Override
//            public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
//                throws CertificateException {
//
//            }
//
//            @Override
//            public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
//                throws CertificateException {
//
//            }
//
//            @Override
//            public X509Certificate[] getAcceptedIssuers() {
//              return new X509Certificate[0];
//            }
//          }},null);
//          SSLSocketFactory socketFactory=sslContext.getSocketFactory();
//
//          if(urlConnection instanceof HttpsURLConnection){
//            HttpsURLConnection httpsURLConnection=(HttpsURLConnection)urlConnection;
//            httpsURLConnection.setSSLSocketFactory(socketFactory);
//            httpsURLConnection.setHostnameVerifier((s,l)->true);
//          }
//
//          return (HttpURLConnection)urlConnection;
//
//        }catch (Exception e){
//          throw new RuntimeException(e);
//        }
//      }
//    };
    Cas20ServiceTicketValidator cas20ServiceTicketValidator = new Cas20ServiceTicketValidator(
        ymlProperties.getCasServerUrl());
    cas20ServiceTicketValidator.setEncoding("UTF-8");
    return cas20ServiceTicketValidator;
  }


  @Bean
  public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    SessionAuthenticationStrategy sessionAuthenticationStrategy = new SessionFixationProtectionStrategy();
    return sessionAuthenticationStrategy;
  }


  /**
   * 此过滤器向cas发送登出请求
   */
  @Bean
  public SingleSignOutFilter singleSignOutFilter() {
    SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
    singleSignOutFilter.setCasServerUrlPrefix(ymlProperties.getCasServerUrl());
    singleSignOutFilter.setIgnoreInitConfiguration(true);
    return singleSignOutFilter;
  }

  /**
   * 此过滤器拦截客户端的logout请求,发现logout请求后向cas服务器发送登出请求
   */
  @Bean
  public LogoutFilter casLogoutFilter() {
    LogoutFilter logoutFilter = new LogoutFilter(ymlProperties.getCasServerLogoutUrl(),
        new SecurityContextLogoutHandler());
    logoutFilter.setFilterProcessesUrl(ymlProperties.getAppLogoutUrl());
    return logoutFilter;
  }

  /**
   * 去除@Secured的前缀 "ROLE_"
   * @return
   */
  @Bean
  public GrantedAuthorityDefaults grantedAuthorityDefaults() {
    return new GrantedAuthorityDefaults("");
  }

}

新建HelloController

@Slf4j
@RestController
public class HelloController {

  @Secured("security_index") 
  //@PreAuthorize("hasAnyAuthority('security_index')")
  @GetMapping("/hello")
  public String hello() {
    return "首页";
  }
}

浏览器访问http://localhost:8080/test/login 如果未登录会跳转到CAS服务端登录页。


前后端分离项目遇到的问题

对于前后端分离面临的问题是后端不干涉前端页面跳转,在退出登录后,访问前端页面仍可以访问,前端向后端发送请求后端拦截重定向到CAS服务端地址,但是前端页面跳转失败。虽说页面数据是不会加载,但是这不符合我们希望实现的。

目前想到通过实现AuthenticationEntryPoint并重定向到指定接口,返回页面
CAS服务端跳转地址,前端通过拦截器在每个页面访问时都会向后端指定接口发送请求,如果没有登录就会返回CAS服务端地址,登录后直接放行,具体判断我们通过返回状态码来实现。

新建AuthController

方法中Result类是自定义的统一返回json格式,我会在下面贴出来。

@Slf4j
@RestController
public class HelloController {

  @Autowired
  private YmlProperties ymlProperties;

  /**
   * 适用前后端分离
   * 当未登录时重定向到此请求,返回给前端CAS服务器登录地址,通过前端跳转
   * @return
   */
  @GetMapping("/send")
  public Result send() {
    String url =
        ymlProperties.getCasServerLoginUrl() + "?service=" + ymlProperties.getAppServerUrl()
            + ymlProperties.getAppLoginUrl();
    return Result.failed().setCode(444).setData(url).setMessage("未登录").setSuccess(false);
  }

  /**
   * 适用前后端分离
   * 当登录成功后返回前端数据
   * @return
   */
  @GetMapping("/login")
  public Result login(){
    return Result.success(null,"已登录");
  }
}

Result统一返回json格式

对于Result类中静态方法可以自己扩展。

public interface IErrorCode {
    long getCode();
    String getMessage();
}
public enum ResultCode implements IErrorCode {
    FAILED(500,"操作失败"),
    SUCCESS(200,"操作成功");
    private long code;
    private String message;

    private ResultCode(long code,String message){
        this.code=code;
        this.message=message;
    }

    @Override
    public long getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
@Data
@Accessors(chain = true)
public class Result<T> {
    private boolean success;
    private long code;
    private String message;
    private T data;

    private Result() {
    }
    private Result(boolean success,long code,String message,T data){
        this.success=success;
        this.code=code;
        this.message=message;
        this.data=data;
    }

    /**
     * 成功返回结果
     * @param data  获取的数据
     */
    public static <T> Result<T> success(T data){
        return new Result<T>(true,ResultCode.SUCCESS.getCode(),ResultCode.SUCCESS.getMessage(),data);
    }

    /**
     * 成功返回结果
     * @param data  获取的数据
     * @param message   提示信息
     */
    public static <T> Result<T> success(T data,String message){
        return new Result<>(true,ResultCode.SUCCESS.getCode(),message,data);
    }

    /**
     * 失败返回结果
     * @param <T>
     * @return
     */
    public static <T> Result<T> failed(){
        return new Result<>(false,ResultCode.FAILED.getCode(),ResultCode.FAILED.getMessage(),null);
    }

    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> Result<T> failed(String message){
        return new Result<>(false,ResultCode.FAILED.getCode(),message,null);
    }
}

新建MyCasAuthenticationEntryPoint类实现AuthenticationEntryPoint

@Component
public class MyCasAuthenticationEntryPoint implements AuthenticationEntryPoint {

  @Autowired
  private YmlProperties ymlProperties;

  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response,
      AuthenticationException authException) throws IOException, ServletException {
    response.sendRedirect(ymlProperties.getAppServerUrl()+"/send");
  }
}

重写SecurityConfiguration配置类

@Slf4j
@EnableWebSecurity
//开启@Secured和@PreAuthorize
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Autowired
  private YmlProperties ymlProperties;

  @Autowired
  private MyCasAuthenticationEntryPoint myCasAuthenticationEntryPoint;

  @Autowired
  private MyUserDetailsService myUserDetailsService;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests() //配置安全策略
        .antMatchers("/login","/send").permitAll() 
        .anyRequest().authenticated() //所有请求都要验证
        .and()
        .csrf().disable()
        .logout().permitAll() //logout不需要验证
        .and()
        .headers().frameOptions().disable()
        .and()
        .cors()
        .and().formLogin(); //使用form表单登录

    http.exceptionHandling().authenticationEntryPoint(myCasAuthenticationEntryPoint)
        .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());
  }

  /**
   * 主要配置的是ServiceProperties的service属性,它指定的是cas回调的地址
   */
  @Bean
  public ServiceProperties serviceProperties() {
    ServiceProperties serviceProperties = new ServiceProperties();
    serviceProperties.setService(ymlProperties.getAppServerUrl() + ymlProperties.getAppLoginUrl());
    serviceProperties.setSendRenew(false);
    serviceProperties.setAuthenticateAllArtifacts(true);
    return serviceProperties;
  }

  /**
   * 认证的入口,指定cas服务器的登录地址,指定ServiceProperties(主要是获取回调地址)
   */
//  @Bean
//  public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
//    CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
//    casAuthenticationEntryPoint
//        .setLoginUrl(casProperties.getCasServerLoginUrl());
//    casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
//    return casAuthenticationEntryPoint;
//  }


  @Bean
  public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
    CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
    casAuthenticationFilter.setServiceProperties(serviceProperties());
    casAuthenticationFilter.setFilterProcessesUrl(ymlProperties.getAppLoginUrl());
    casAuthenticationFilter.setAuthenticationManager(authenticationManager());

    //指定登录成功后跳转页面,也可以使用SavedRequestAwareAuthenticationSuccessHandler
//    SavedRequestAwareAuthenticationSuccessHandler handler=new SavedRequestAwareAuthenticationSuccessHandler();
//    handler.setRedirectStrategy(new MyRedirectStrategy());
//    casAuthenticationFilter.setAuthenticationSuccessHandler(handler);
    casAuthenticationFilter.setAuthenticationSuccessHandler(
        new SimpleUrlAuthenticationSuccessHandler(
            ymlProperties.getAppServerUrl() + "/hello"));
    casAuthenticationFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
    return casAuthenticationFilter;
  }

//  class MyRedirectStrategy extends DefaultRedirectStrategy {
//
//    @Override
//    public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
//        throws IOException {
//      String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
//      redirectUrl = response.encodeRedirectURL(redirectUrl);
//      log.info(redirectUrl);
//      if(redirectUrl.startsWith("http://")){
//        if(redirectUrl.contains("/app/login")){
//          redirectUrl="http://10.11.36.21:8080/test/hello";
//        }
//      }
//      response.sendRedirect(redirectUrl);
//    }
//  }


  @Bean
  public CasAuthenticationProvider casAuthenticationProvider() {
    CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
    casAuthenticationProvider.setServiceProperties(serviceProperties());
    casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
    casAuthenticationProvider
        .setAuthenticationUserDetailsService(myUserDetailsService);
    casAuthenticationProvider.setKey("casAuthenticationProviderKey");
    return casAuthenticationProvider;
  }


  /**
   * 验证ticker,向cas服务器发送验证请求
   */
  @Bean
  public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
    //转Https请求
//    HttpURLConnectionFactory httpURLConnectionFactory=new HttpURLConnectionFactory() {
//      @Override
//      public HttpURLConnection buildHttpURLConnection(URLConnection urlConnection) {
//        SSLContext sslContext=null;
//        try {
//          sslContext = SSLContext.getInstance("SSL");
//          sslContext.init(new KeyManager[]{},new TrustManager[]{new X509TrustManager() {
//            @Override
//            public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
//                throws CertificateException {
//
//            }
//
//            @Override
//            public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
//                throws CertificateException {
//
//            }
//
//            @Override
//            public X509Certificate[] getAcceptedIssuers() {
//              return new X509Certificate[0];
//            }
//          }},null);
//          SSLSocketFactory socketFactory=sslContext.getSocketFactory();
//
//          if(urlConnection instanceof HttpsURLConnection){
//            HttpsURLConnection httpsURLConnection=(HttpsURLConnection)urlConnection;
//            httpsURLConnection.setSSLSocketFactory(socketFactory);
//            httpsURLConnection.setHostnameVerifier((s,l)->true);
//          }
//
//          return (HttpURLConnection)urlConnection;
//
//        }catch (Exception e){
//          throw new RuntimeException(e);
//        }
//      }
//    };
    Cas20ServiceTicketValidator cas20ServiceTicketValidator = new Cas20ServiceTicketValidator(
        ymlProperties.getCasServerUrl());
    cas20ServiceTicketValidator.setEncoding("UTF-8");
    return cas20ServiceTicketValidator;
  }


  @Bean
  public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    SessionAuthenticationStrategy sessionAuthenticationStrategy = new SessionFixationProtectionStrategy();
    return sessionAuthenticationStrategy;
  }


  /**
   * 此过滤器向cas发送登出请求
   */
  @Bean
  public SingleSignOutFilter singleSignOutFilter() {
    SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
    singleSignOutFilter.setCasServerUrlPrefix(ymlProperties.getCasServerUrl());
    singleSignOutFilter.setIgnoreInitConfiguration(true);
    return singleSignOutFilter;
  }

  /**
   * 此过滤器拦截客户端的logout请求,发现logout请求后向cas服务器发送登出请求
   */
  @Bean
  public LogoutFilter casLogoutFilter() {
    LogoutFilter logoutFilter = new LogoutFilter(ymlProperties.getCasServerLogoutUrl(),
        new SecurityContextLogoutHandler());
    logoutFilter.setFilterProcessesUrl(ymlProperties.getAppLogoutUrl());
    return logoutFilter;
  }

  /**
   * 取出@Secured的前缀 "ROLE_"
   * @return
   */
  @Bean
  public GrantedAuthorityDefaults grantedAuthorityDefaults() {
    return new GrantedAuthorityDefaults("");
  }

}

前端只需要访问/login请求,登录就会返回200状态码和“已登录”提示信息,未登录后端就会重定向/send请求,并返回444状态码和CAS服务端地址,前端根据444状态码拿到CAS服务端地址并跳转页面。

注意未登录时后端会重定向一次,所以想一想前端如何拿到重定向的数据。



对于以上有什么建议,请在评论区留下宝贵意见!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值