Spring Boot 自定义接入 Cas 认证
一、快速接入
第1步:引入依赖
在项目依赖管理中引入 cas-client-core 依赖 :
Maven 依赖
<!-- https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core -->
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.6.4</version>
</dependency>
Gradle 依赖
// https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core
implementation group: 'org.jasig.cas.client', name: 'cas-client-core', version: '3.6.4'
第2步:功能扩展
1、默认的配置项,不足以实现基于Cas登录和登出流程,这里需要自定义一些扩展的配置
自定义配置对象 CasClientCustomProperties:
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
/**
* CAS客户端扩展配置
*/
@ConfigurationProperties(prefix = CasClientProperties.PREFIX)
@Data
@RefreshScope
public class CasClientProperties {
public static final String PREFIX = "cas";
/**
* CAS-protected client Web Page host URL
* E.g. https://myclient.example.com Required.
*/
@Value("${cas.client-host-proxy-url}")
private String clientHostProxyUrl;
/**
* CAS-protected client Login Api URL
* E.g: https://myclient.example.com/cas/login
*/
@Value("${cas.client-login-url}")
private String clientLoginUrl;
/**
* CAS-protected client Logout Api URL
* E.g: https://myclient.example.com/cas/logout
*/
@Value("${cas.client-logout-url}")
private String clientLogoutUrl;
/**
* CAS-protected client Ignore Pattern Path
* E.g: /api/*|/auth/*
*/
@Value("${cas.ignore-pattern-path:}")
private String ignorePatternPath;
}
/**
* 客户端配置类
*/
public class CASProperty {
/**
* cas server 域名 https://cas_server_url
*/
private String serverUrlPrefix;
/**
* cas server 登录地址 https://cas_server_url/login
*/
private String serverLoginUrl;
/**
* 客户端首页地址 http://cas_client_url
*/
private String clientHostUrl;
/**
* 客户端退出页 https://cas_server_url/logout?service=http://client.xxxxx.com/casLogin
*/
private String clientLogoutUrl;
/**
* 不需要拦截的url前缀 /api/*|/auth/*
*/
private String ignorePatternPath;
}
@Bean
public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<>();
listener.setListener(new SingleSignOutHttpSessionListener());
listener.setOrder(1);
return listener;
}
/**
* 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前
* @return
*/
@Bean
public FilterRegistrationBean singleSignOutFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new SingleSignOutFilter());
filterRegistration.addUrlPatterns("/*"); filterRegistration.addInitParameter("casServerUrlPrefix", casProperty.getServerUrlPrefix());
filterRegistration.setOrder(1);
return filterRegistration;
}
/**
* 该过滤器负责用户的认证工作
* @return
*/
@Bean
public FilterRegistrationBean authenticationFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new AuthenticationFilter());
filterRegistration.addUrlPatterns("/*");
//不需要拦截的url前缀
filterRegistration.addInitParameter("ignorePattern", casProperty.getIgnorePatternPath); filterRegistration.addInitParameter("casServerLoginUrl", casProperty.getServerLoginUrl()); filterRegistration.addInitParameter("serverName", casProperty.getClientHostUrl());
filterRegistration.addInitParameter("useSession", "true"); filterRegistration.addInitParameter("redirectAfterValidation", "true");
filterRegistration.setOrder(2);
return filterRegistration;
}
/**
* 该过滤器负责对Ticket的校验工作,使用CAS 3.0协议
* @return
*/
@Bean
public FilterRegistrationBean cas30ProxyReceivingTicketValidationFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
filterRegistration.addUrlPatterns("/*");
filterRegistration.addInitParameter("casServerUrlPrefix", casProperty.getServerUrlPrefix());
filterRegistration.addInitParameter("serverName", casProperty.getClientHostUrl());
filterRegistration.setOrder(3);
return filterRegistration;
}
/**
* 所有请求追加 request对象,和使用request.getRemoteUser()获取登录用户
*
* @return
*/
@Bean
public FilterRegistrationBean httpServletRequestWrapperFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new HttpServletRequestWrapperFilter());
filterRegistration.addUrlPatterns("/*");
filterRegistration.setOrder(4);
return filterRegistration;
}
@Bean
public FilterRegistrationBean assertionThreadLocalFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new AssertionThreadLocalFilter());
filterRegistration.addUrlPatterns("/*");
filterRegistration.setOrder(5);
return filterRegistration;
}
前后端分离的项目
后端服务配置同上,还需要增加中转接口,将前后端的会话id统一。以 spring boot + spring mvc + vue 项目为例增加中转接口,登录成功后跳转至前端首页,将后端jsessionid传递给前端。
@GetMapping("/casLogin")
@ApiOperation(value = "登录跳转页")
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession session = request.getSession();
Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
if (assertion != null) {
String jsessionid = request.getSession().getId();
response.sendRedirect(casProperty.getFrontUrl()+"?jsessionid="+jsessionid);
}
}
1 前端需要新增全局拦截器,未登录状态下一律拦截到casLogin接口,登录成功后会将会重定向至配置的前端页面,并追加面jsessionid参数,需要将jsessionid写入cookie,后续所有请求保持和后端jsessionid一致(注意跨域)
2 后端需要修改默认拦击器, CustomAuthenticationFilter 将未登录跳转登录页面的逻辑代码修改为返回401状态码,前端根据状态码,自行跳转页面登录
@Bean
public FilterRegistrationBean authenticationFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new CustomAuthenticationFilter());
filterRegistration.addUrlPatterns("/*");
filterRegistration.addInitParameter("ignorePattern", casProperty.getIgnorePatternPath());
filterRegistration.addInitParameter("casServerLoginUrl", casProperty.getServerLoginUrl());
filterRegistration.addInitParameter("serverName", casProperty.getClientHostUrl());
filterRegistration.addInitParameter("useSession", "true");
filterRegistration.addInitParameter("redirectAfterValidation", "true");
filterRegistration.setOrder(4);
return filterRegistration;
}
class CustomAuthenticationFilter extends AbstractCasFilter
doFilter()
将此方法中的跳转逻辑修改为返回401状态码,供前端获取拦截
统一登出:
修改项目的退出方法为调用cas server的退出
@ApiOperation(value = "退出系统")
@GetMapping("casLogout")
public String logout(HttpServletRequest request) {
request.getSession().invalidate();
return "redirect:"+casProperty.getClientLogoutUrl();
}
登录状态下获取用户信息:
直接获取登录用户名
request.getRemoteUser();
获取详细用户信息
AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();
Map attributes = principal.getAttributes();
Object moblie=attributes .get("moblie");
作者:Jeebiz 创建时间:2023-09-26 13:06
最后编辑:Jeebiz 更新时间:2024-05-07 20:29
最后编辑:Jeebiz 更新时间:2024-05-07 20:29