cas单点登录原理与实现(整合springsecurity)

一、cas原理分析

SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS是一种基于http协议的B/S应用系统单点登录实现方案,认识CAS之前首先要熟悉http协议、Session与Cookie等Web开发基本知识。

1.1 Cas登录:
两次前端跳转、一次后端验证

1.1.1 首次访问应用A

第一次跳转:

客户端访问应用系统A,应用系统判断Session发现未登录,返回302跳转到sso登录页面,并传递service参数给sso,该service参数有两个作用:(回跳、认证)
1、 service一般传递应用系统url地址,用于sso认证通过后回跳到应用系统A;
2、service参数同时会被cas服务端的作为cas客户端的唯一标记记录下来,用于后期匹配相应的认证凭据;

第二次跳转:

浏览器显示登录页面,用户输入账号密码登录成功后,sso会返回302跳转回到原来请求的应用系统页面,并携带ticket参数,作为认证票据,同时通过Set-Cookie向浏览器记录TGT,(TGT的作用将在下一个应用系统需要登录的时候体现出作用,是避免重复登录的关键)

后台进行一次票据验证:

应用系统接收到带有ticket的请求后,从后台直接向sso服务器发起一个http请求,将service和ticket作为参数,用于验证ticket的有效性;如果ticket有效,sso服务器将返回该ticket对应的登录用户名。

图例:

在这里插入图片描述
1.1.2 访问A登陆后首次访问应用B

访问应用B系统,根据session判断未登录,重定向到CAS Serve,根据Cookie里面的TGT找到对应的用户信息,携带ticket重定向会应用系统B,应用系统B接收到带有ticket的请求后,从后台直接向sso服务器发起一个http请求,将service和ticket作为参数,用于验证ticket的有效性;如果ticket有效,sso服务器将返回该ticket对应的登录用户名

二、实现cas客户端

yaml配置:

security:
  cas:
    server:
      host: https://localhost:8443/cas  #cas服务器的地址
      login: ${security.cas.server.host}/login  #单点登录地址
      logout: ${security.cas.server.host}/logout  #单点登出店址
    service:
      webHost: http://localhost:8090  #应用系统的地址
      login: /login       #应用系统的登录入口
      logout: /logout     #应用系统的登出入口

2.1 参数配置类:

  • CAS认证中心参数配置类
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
public class CasServerConfig {

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

  @Value("${security.cas.server.login}")
  private String login;

  @Value("${security.cas.server.logout}")
  private String logout;
}
  • 应用服务器参数配置类
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
public class CasServiceConfig {

  @Value("${security.cas.service.webHost}")
  private String webHost;

  @Value("${security.cas.service.login}")
  private String login;

  @Value("${security.cas.service.logout}")
  private String logout;

  private Boolean sendRenew = false;

}

2.2 CAS配置

  • CAS配置类
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.web.authentication.logout.LogoutFilter;

@Configuration
public class SecurityConfiguration {

  @Autowired
  private CasServerConfig casServerConfig;

  @Autowired private CasServiceConfig casServiceConfig;

  @Bean
  public ServiceProperties serviceProperties() {
    ServiceProperties serviceProperties = new ServiceProperties();
    serviceProperties.setService(
    this.casServiceConfig.getWebHost() + this.casServiceConfig.getLogin()); //回跳地址
    serviceProperties.setSendRenew(this.casServiceConfig.getSendRenew());//是否敏感,即登录不同的应用系统是否还要重新登录
   
    serviceProperties.setAuthenticateAllArtifacts(true); //是否对没有ticket的访问需要验证
    return serviceProperties;
  }

  /***
   * CAS认证过滤器
   * @param authenticationManager
   * @param serviceProperties
   * @return
   */
  @Bean
  public CasAuthenticationFilter casAuthenticationFilter(
          AuthenticationManager authenticationManager, ServiceProperties serviceProperties) {
    CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
    casAuthenticationFilter.setAuthenticationManager(authenticationManager);
    casAuthenticationFilter.setServiceProperties(serviceProperties);
    casAuthenticationFilter.setFilterProcessesUrl(
        this.casServiceConfig.getLogin());
    casAuthenticationFilter.setContinueChainBeforeSuccessfulAuthentication(false);
    casAuthenticationFilter.setAuthenticationSuccessHandler(
        new UrlAuthenticationSuccessHandler("/"));
    return casAuthenticationFilter;
  }


/**
   * CAS入口
   * @param serviceProperties
   * @return
   */
  @Bean
  public CasAuthenticationEntryPoint casAuthenticationEntryPoint(
      ServiceProperties serviceProperties) {
    CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
    entryPoint.setLoginUrl(this.casServerConfig.getLogin());
    entryPoint.setServiceProperties(serviceProperties);
    return entryPoint;
  }


  /**
   * CASticket验证
   * @return
   */
  @Bean
  public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
    return new Cas20ServiceTicketValidator(this.casServerConfig.getHost());
  }

  @Bean
  public CasAuthenticationProvider casAuthenticationProvider(
      AuthenticationUserDetailsService<CasAssertionAuthenticationToken> userDetailsService,
      ServiceProperties serviceProperties,
      Cas20ServiceTicketValidator ticketValidator) {
    CasAuthenticationProvider provider = new CasAuthenticationProvider();
    provider.setKey("casProvider");
    provider.setServiceProperties(serviceProperties);
    provider.setTicketValidator(ticketValidator);
    provider.setAuthenticationUserDetailsService(userDetailsService);//自己的userDetailService

    return provider;
  }

 /**
   * 登出过滤器
   * @return
   */
  @Bean
  public LogoutFilter logoutFilter() {
    String logoutRedirectPath =
        this.casServerConfig.getLogout() + "?service=" + this.casServiceConfig.getWebHost();
    LogoutFilter logoutFilter =
        new LogoutFilter(logoutRedirectPath, new CasSecurityContextLogoutHandler());
    logoutFilter.setFilterProcessesUrl(this.casServiceConfig.getLogout());
    return logoutFilter;
  }
}
  • 认证成功处理类
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Collection;

@Slf4j(topic = "c.successHandler")
public class UrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    public UrlAuthenticationSuccessHandler() {
        super();
    }


    public UrlAuthenticationSuccessHandler(String defaultTargetUrl) {
        super(defaultTargetUrl);
    }

    /**
     * 认证成功后
     * @param request
     * @param response
     * @param authentication
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {


        super.onAuthenticationSuccess(request, response, authentication);
    }

    /**
     *返回一个认证成功后的路径
     * @param request
     * @param response
     * @return
     */

    @Override
    protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
        StringBuffer targetUrl = null;
        //根据自己需求写
        HttpSession session = request.getSession();
        targetUrl = (StringBuffer) session.getAttribute("REQUEST_URL");
        log.debug("目标路径为{}", targetUrl);
        return targetUrl.toString();
    }
}

  • 自定义过滤器(存储要访问的路径)
mport com.hzx.hzxy_grid.domain.SecurityUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Collection;

@Component
@Slf4j
public class HttpParamsFilter implements Filter {


  @Override
  public void init(FilterConfig filterConfig) throws ServletException {}

  @Override
  public void doFilter(
          ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
      throws IOException, ServletException {
      final HttpServletRequest request = (HttpServletRequest) servletRequest;
      final HttpServletResponse response = (HttpServletResponse) servletResponse;
      HttpSession session = request.getSession();
      StringBuffer requestURL = request.getRequestURL();
      log.info("请求地址:" + requestURL);

    chain.doFilter(request, response);
  }

  @Override
  public void destroy() {}
}

  • 单点登出过滤器
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class CasSecurityContextLogoutHandler implements LogoutHandler {
  protected final Log logger = LogFactory.getLog(this.getClass());
  private boolean invalidateHttpSession = true;
  private boolean clearAuthentication = true;

  public CasSecurityContextLogoutHandler() {
  }

  public void logout(
          HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
    Assert.notNull(request, "HttpServletRequest required");
    if (this.invalidateHttpSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
        this.logger.debug("Invalidating session: " + session.getId());
        //发布退出登录事件,自己增加,监听此事件处理一些事情
        session.invalidate(); //session失效
//        LogoutEvent event = new LogoutEvent();
//        event.setSessionId(session.getId());
//        EventPublisherUtil.publish(event);
      }
    }

    if (this.clearAuthentication) {
      SecurityContext context = SecurityContextHolder.getContext();
      context.setAuthentication((Authentication)null);
    }

    SecurityContextHolder.clearContext();
  }

  public boolean isInvalidateHttpSession() {
    return this.invalidateHttpSession;
  }

  public void setInvalidateHttpSession(boolean invalidateHttpSession) {
    this.invalidateHttpSession = invalidateHttpSession;
  }

  public void setClearAuthentication(boolean clearAuthentication) {
    this.clearAuthentication = clearAuthentication;
  }
}

  • springsecurity配置类
import com.hzx.hzxy_grid.config.custom.CustomFilter;
import com.hzx.hzxy_grid.config.custom.CustomUrlDecisionManager;
import com.hzx.hzxy_grid.handler.CustomizeAccessDeniedHandler;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsUtils;

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class CasWebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private CasAuthenticationEntryPoint casAuthenticationEntryPoint;

    @Autowired
    private CasAuthenticationProvider casAuthenticationProvider;

    @Autowired
    private CasAuthenticationFilter casAuthenticationFilter;

    @Autowired
    private LogoutFilter logoutFilter;

    @Autowired
    private CasServerConfig casServerConfig;

    @Autowired
    private HttpParamsFilter httpParamsFilter;

    @Autowired
    private CustomFilter customFilter;

    @Autowired
    private CustomUrlDecisionManager customUrlDecisionManager;



    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/swagger-resources/**",
                "/swagger-ui.html",
                "/v2/api-docs",
                "/webjars/**",
                "/v3/api-docs",
//                "/login",
                "/static/**",
                "/api/**"

        );

        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable();

        http.csrf().disable();
        http.authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest)
                .permitAll()
                .anyRequest()
                .authenticated(); // 所有资源都需要登陆后才可以访问。

        http.logout().permitAll(); // 不拦截注销

        http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint)
                .accessDeniedHandler(new CustomizeAccessDeniedHandler())
        ;
        // 单点注销的过滤器,必须配置在SpringSecurity的过滤器链中,如果直接配置在Web容器中,貌似是不起作用的。我自己的是不起作用的。
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setArtifactParameterName(this.casServerConfig.getHost());

        http.addFilter(casAuthenticationFilter)
                .addFilterBefore(logoutFilter, LogoutFilter.class)
                .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class);
        http.addFilterBefore(httpParamsFilter, FilterSecurityInterceptor.class);
        http.addFilterBefore(filterSecurityInterceptor(), FilterSecurityInterceptor.class);
        http.antMatcher("/**");
    }

    /**
     * 权限拦截
     *
     * @return
     * @throws Exception
     */
    public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception {
        FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
        filterSecurityInterceptor.setSecurityMetadataSource(customFilter);
        filterSecurityInterceptor.setAuthenticationManager(authenticationManager());
        filterSecurityInterceptor.setAccessDecisionManager(customUrlDecisionManager);
        return filterSecurityInterceptor;
    }


    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(casAuthenticationProvider);
    }

    @Bean
    public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener>
    singleSignOutHttpSessionListener() {
        ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> servletListenerRegistrationBean =
                new ServletListenerRegistrationBean<>();
        servletListenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener());
        return servletListenerRegistrationBean;
    }


    /**
     * AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}
  • CustomFilter类
/**
 * 根据访问的路径判断出所需的角色
 */

@Component
@Slf4j(topic = "c.CustomUrl")
public class CustomFilter implements FilterInvocationSecurityMetadataSource {

    //路径匹配
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private MenuService menuService;


    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        // 获取请求的Url
        String requestUrl = ((FilterInvocation) o).getRequestUrl();
        List<Menu> menuWithRoles = menuService.getMenuWithRoles();
        for (Menu menu : menuWithRoles) {
            //访问的路径是否跟菜单的里的路径相同
            if (antPathMatcher.match(menu.getUrl(), requestUrl)) {

                String[] roles = menu.getRoleList().stream().map(Role::getRoleName).toArray(String[]::new);
                log.info("需要的权限为{}", roles);
                System.out.println(roles);
                return SecurityConfig.createList(roles);
            }
        }


        return SecurityConfig.createList("ROLE_LOGIN");
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}

  • CustomDecisionManager
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : collection) {
            String needRole = configAttribute.getAttribute();
            if ("ROLE_LOGIN".equals(needRole)) {
                if (authentication instanceof AnonymousAuthenticationToken) {
                    throw new AccessDeniedException("用户未登录,请登录");
                } else
                    return;
            }

            //判断是否有对应的权限信息
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if(authority.getAuthority().equals(needRole))
                    return;
            }
        }
        throw new AccessDeniedException("用户无权限访问");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}

  • 未授权无法访问CustomizeAccessDeniedHandler
import com.hzx.hzxy_grid.domain.R;
import com.hzx.hzxy_grid.enums.Status;
import com.hzx.hzxy_grid.util.ResponseUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 未授权,用户无权访问
 */

public class CustomizeAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        ResponseUtil.out(httpServletResponse,R.error().status(Status.UNAUTHORIZED));
    }
}
  • AuthenticationUserDetailsService
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hzx.hzxy_grid.domain.Admin;
import com.hzx.hzxy_grid.domain.Role;
import com.hzx.hzxy_grid.domain.SecurityUser;
import com.hzx.hzxy_grid.mapper.AdminMapper;
import com.hzx.hzxy_grid.mapper.RoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


@Slf4j(topic = "c.UserDetailServiceImpl")
@Service
public class MyUserDetailServiceImpl implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {

        String username = token.getName();
        Admin admin = adminMapper.selectOne(new LambdaQueryWrapper<Admin>().eq(Admin::getUsername, username));
        log.info("admin:{}", admin);
        if (admin == null)
            throw new UsernameNotFoundException("用户名不存在");
        SecurityUser securityUser = new SecurityUser();


        List<String> permissionList = null;

        //如果redis不存在该用户的权限
        if (!redisTemplate.hasKey("permission_" + admin.getUid())) {
            //添加权限
            List<Role> roleList = roleMapper.getRolesByAdminId(admin.getUid());
            permissionList = roleList.stream().map(role -> role.getRoleName()).collect(Collectors.toList());
            securityUser.setPermissionValueList(permissionList);
            securityUser.setCurrentUserInfo(admin);
            log.info("从数据库中查询到用户的权限为{}", permissionList);
        } else {

            permissionList = (List<String>) redisTemplate.opsForValue().get("permission_" + admin.getUid());
            log.info("从redis中查询到用户的权限为{}",permissionList);
        }

        securityUser.setPermissionValueList(permissionList);
        securityUser.setCurrentUserInfo(admin);
        log.info("用户的权限为{}", permissionList);
        return securityUser;
    }
}

  • SecurityUser
import lombok.Data;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Data
public class SecurityUser implements UserDetails, CredentialsContainer {

    //当前登录用户
    private transient Admin currentUserInfo;

    //当前权限
    private List<String> permissionValueList;

    public SecurityUser() {
    }

    public SecurityUser(Admin admin) {
        if (admin != null) {
            this.currentUserInfo = admin;
        }
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for(String permissionValue : permissionValueList) {
            if(StringUtils.isEmpty(permissionValue)) continue;
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities.add(authority);
        }

        return authorities;
    }

    @Override
    public String getPassword() {
        return currentUserInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return currentUserInfo.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public void eraseCredentials() {
        
    }
}


  • 4
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Spring Security可以与CAS(Central Authentication Service)集成,实现单点登录(SSO)功能。CAS是一个开源的单点登录协议,它通过一个中心认证服务系统来管理用户的登录状态。在CAS系统中,有CAS Server和CAS Client两部分。 要使用Spring Security实现CAS单点登录,需要进行以下步骤: 1. 添加CAS依赖:在项目的构建文件中添加CAS相关的依赖,例如Maven的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-cas</artifactId> <version>5.4.2</version> </dependency> ``` 2. 配置CAS Server和CAS Client:在Spring Security的配置文件中,配置CAS Server和CAS Client的相关信息。例如,在application.properties文件中添加以下配置: ```properties # CAS Server配置 cas.server.url.prefix=https://cas.example.com:8443/cas cas.server.login.url=https://cas.example.com:8443/cas/login cas.server.logout.url=https://cas.example.com:8443/cas/logout # CAS Client配置 cas.client.server-url-prefix=https://your-application.com cas.client.service-url=https://your-application.com/login/cas cas.client.logout-url=https://your-application.com/logout # 其他Spring Security配置 spring.security.user.name=user spring.security.user.password=password spring.security.user.roles=USER ``` 3. 配置Spring Security过滤器链:在Spring Security的配置类中,配置CAS认证过滤器链。例如,在SecurityConfig.java文件中添加以下配置: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .logout() .logoutSuccessUrl("/") .and() .addFilter(casAuthenticationFilter()); } @Bean public CasAuthenticationFilter casAuthenticationFilter() throws Exception { CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter(); casAuthenticationFilter.setAuthenticationManager(authenticationManager()); return casAuthenticationFilter; } // 其他配置... } ``` 4. 创建登录和注销页面:在应用程序中创建登录和注销页面,以便用户进行CAS单点登录和注销操作。 通过以上步骤,就可以使用Spring Security实现CAS单点登录了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值