springboot整个cas_SpringBoot集成SpringSecurity+CAS

本文详细介绍了如何在SpringBoot项目中利用SpringSecurity和CAS实现单点登录和注销功能,包括配置CAS服务和自己的服务,设置认证过滤器、注销过滤器以及处理回跳地址的问题。

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

1 简介

本文主要讲述如何通过SpringSecurity+CAS在springboot项目中实现单点登录和单点注销的功能。

2 项目依赖

主要依赖如下

org.springframework.boot

spring-boot-starter-security

org.springframework.security

spring-security-cas

org.projectlombok

lombok

true

3 项目配置

Application配置。

@SpringBootApplication(scanBasePackages = "com.wawscm")

@EnableWebSecurity

public class Application {

public static void main(String[] args) {

new SpringApplicationBuilder(Application.class).web(true).run(args);

}

}

增加CAS参数配置

这里分为CASServer配置和CASService配置。其中Server是CAS服务的配置,Service是我们自己服务的配置。

@Data

@ConfigurationProperties(prefix = "security.cas.server")

public class CasServerConfig {

private String host;

private String login;

private String logout;

}

@Data

@ConfigurationProperties(prefix = "security.cas.service")

public class CasServiceConfig {

private String host;

private String login;

private String logout;

private Boolean sendRenew = false;

}

配置内容如下

security:

cas:

server:

host: http://192.168.1.202:9082/cas

login: ${security.cas.server.host}/login

logout: ${security.cas.server.host}/logout

service:

host: http://localhost:9088

login: /login/cas

logout: /logout

后面需要根据实际配置再拼接参数。

SpringSecurity Bean配置

@Configuration

@EnableConfigurationProperties({CasServerConfig.class, CasServiceConfig.class})

public class SecurityConfiguration {

@Autowired

private CasServerConfig casServerConfig;

@Autowired

private CasServiceConfig casServiceConfig;

@Bean

public ServiceProperties serviceProperties() {

ServiceProperties serviceProperties = new ServiceProperties();

serviceProperties.setService(this.casServiceConfig.getHost() + this.casServiceConfig.getLogin());

serviceProperties.setSendRenew(this.casServiceConfig.getSendRenew());

return serviceProperties;

}

@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 SimpleUrlAuthenticationSuccessHandler("/")

);

return casAuthenticationFilter;

}

@Bean

public CasAuthenticationEntryPoint casAuthenticationEntryPoint(ServiceProperties serviceProperties) {

CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();

entryPoint.setLoginUrl(this.casServerConfig.getLogin());

entryPoint.setServiceProperties(serviceProperties);

return entryPoint;

}

@Bean

public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {

return new Cas20ServiceTicketValidator(this.casServerConfig.getHost());

}

@Bean

public CasAuthenticationProvider casAuthenticationProvider(

AuthenticationUserDetailsService userDetailsService,

ServiceProperties serviceProperties, Cas20ServiceTicketValidator ticketValidator) {

CasAuthenticationProvider provider = new CasAuthenticationProvider();

provider.setKey("casProvider");

provider.setServiceProperties(serviceProperties);

provider.setTicketValidator(ticketValidator);

provider.setAuthenticationUserDetailsService(userDetailsService);

return provider;

}

@Bean

public LogoutFilter logoutFilter() {

String logoutRedirectPath = this.casServerConfig.getLogout() + "?service=" + this.casServiceConfig.getHost();

LogoutFilter logoutFilter = new LogoutFilter(logoutRedirectPath, new SecurityContextLogoutHandler());

logoutFilter.setFilterProcessesUrl(this.casServiceConfig.getLogout());

return logoutFilter;

}

}

ServiceProperties :服务配置,我们自己的服务。

CasAuthenticationFilter:CAS认证过滤器,主要实现票据认证和认证成功后的跳转。

LogoutFilter:注销功能

Spring Security配置

@Configuration

@Order(SecurityProperties.ACCESS_OVERRIDE_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;

@Override

protected void configure(HttpSecurity http) throws Exception {

http.headers().frameOptions().disable();

http.csrf().disable();

http.authorizeRequests()

.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()

.antMatchers("/static/**").permitAll() // 不拦截静态资源

.antMatchers("/api/**").permitAll() // 不拦截对外API

.anyRequest().authenticated(); // 所有资源都需要登陆后才可以访问。

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

http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint);

// 单点注销的过滤器,必须配置在SpringSecurity的过滤器链中,如果直接配置在Web容器中,貌似是不起作用的。我自己的是不起作用的。

SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();

singleSignOutFilter.setCasServerUrlPrefix(this.casServerConfig.getHost());

http.addFilter(casAuthenticationFilter)

.addFilterBefore(logoutFilter, LogoutFilter.class)

.addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class);

http.antMatcher("/**");

}

@Autowired

public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

auth.authenticationProvider(casAuthenticationProvider);

}

@Bean

public ServletListenerRegistrationBean singleSignOutHttpSessionListener(){

ServletListenerRegistrationBean servletListenerRegistrationBean =

new ServletListenerRegistrationBean<>();

servletListenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener());

return servletListenerRegistrationBean;

}

}

到此SpringBoot、SpringSecurity、CAS集成结束。但是这样配置有一个问题,那就是如果我们登录之前的请求是带参数的,或者跳转的不是首页,那么就会出现登录成功之后直接跳转到主页,而不是我们想要访问的页面,参数也丢失了。下面我们来解决这个问题。

4 、处理回跳地址

处理的思路是,在登录之前记住访问地址及请求参数,在登录成功之后再取到这个地址然后回跳到对应的地址。

首先我们需要写一个过滤器来获取我们的请求地址,并放到Session中。

public class HttpParamsFilter implements Filter {

public String REQUESTED_URL = "CasRequestedUrl";

@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();

String requestPath = WebUtils.getFullPath(request);

session.setAttribute(REQUESTED_URL, requestPath);

chain.doFilter(request, response);

}

@Override

public void destroy() {

}

}

然后在CasWebSecurityConfiguration中增加对应的配置。

@BeanpublicFilterRegistrationBean httpParamsFilter() {

FilterRegistrationBean filterRegistrationBean= newFilterRegistrationBean();

filterRegistrationBean.setFilter(newHttpParamsFilter());

filterRegistrationBean.setOrder(-999);

filterRegistrationBean.addUrlPatterns("/");returnfilterRegistrationBean;

}

然后扩展SimpleUrlAuthenticationSuccessHandler来实现我们的功能。

public class MyUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

public NeteaseUrlAuthenticationSuccessHandler() {

super();

}

public NeteaseUrlAuthenticationSuccessHandler(String defaultTargetUrl) {

super(defaultTargetUrl);

}

@Override

protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {

if (isAlwaysUseDefaultTargetUrl()) {

return this.getDefaultTargetUrl();

}

// Check for the parameter and use that if available

String targetUrl = null;

if (this.getTargetUrlParameter() != null) {

targetUrl = request.getParameter(this.getTargetUrlParameter());

if (StringUtils.hasText(targetUrl)) {

logger.debug("Found targetUrlParameter in request: " + targetUrl);

return targetUrl;

}

}

if (!StringUtils.hasText(targetUrl)) {

HttpSession session = request.getSession();

targetUrl = (String) session.getAttribute(HttpParamsFilter.REQUESTED_URL);

}

if (!StringUtils.hasText(targetUrl)) {

targetUrl = this.getDefaultTargetUrl();

logger.debug("Using default Url: " + targetUrl);

}

return targetUrl;

}

}

最后将CasAuthenticationFilter中的SimpleUrlAuthenticationSuccessHandler替换为MyUrlAuthenticationSuccessHandler就可以了。

这里需要注意一个问题,由于CAS回调是访问的/login/cas(这里是我的配置),所以过滤器一定不能拦截/login/cas否则HttpParamsFilter会将/login/cas放到Session中,就出现了无限循环。

1. 访问http://host/?id=1 -- session: /?id=1

2. CAS登录成功,然后回跳到login/cas?ticket=xxx -- session: login/cas?ticket=xxx

3. 验证票据成功NeteaseUrlAuthenticationSuccessHandler处理跳转,从session中获取跳转地址:login/cas?ticket=xxx

4. 跳转到`login/cas?ticket=xxx`然后重复步骤 2-4

主要是我们保留了请求中的参数,所以一直会有票据信息。所以就出现了无限循环。如果没有保留票据信息,就直接报错了,因为第二次访问的时候票据丢了。

由于我的是单页应用,所以我直接拦截主页就可以了。

另一种处理方法是在HttpParamsFilter判断访问地址,如果是login/cas就不更新Session中的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值