上一篇我们试着搭建了一个简单的CAS服务端,在这一篇中,我们将整合Shiro搭建一个CAS客户端。
1 新建Maven项目,引入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>gdou.laixiaoming</groupId>
<artifactId>cas-client-a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cas-client-a</name>
<description>CAS Client Demo.</description>
<properties>
<java.version>1.8</java.version>
<shiro.version>1.4.0</shiro.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2 Shiro配置
扩展CasRealm,在登录成功后,可以获取到登录的用户名,在客户端这边再把用户拥有的角色和权限查询出来并保存:
public class MyCasRealm extends CasRealm{
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取用户名
String username = (String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// authorizationInfo.setRoles(new HashSet<>(Arrays.asList("admin")));
// authorizationInfo.setStringPermissions(new HashSet<>(Arrays.asList("admin")));
return authorizationInfo;
}
}
Shiro配置:
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//配置自定义casFilter
Map<String, Filter> filters = new LinkedHashMap<>();
CasFilter casFilter = new CasFilter();
casFilter.setFailureUrl("/casFailure");
filters.put("cas", casFilter);
shiroFilterFactoryBean.setFilters(filters);
//配置filter调用链
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
//anon为匿名,不拦截
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/casFailure", "anon");
//拦截CAS Server返回的ticket
filterChainDefinitionMap.put("/cas", "cas");
//退出登录
filterChainDefinitionMap.put("/logout", "anon");
filterChainDefinitionMap.put("/logouttips", "anon");
//需要登录访问的页面
filterChainDefinitionMap.put("/**", "user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//service指定了登录成功后的回调地址,回调/cas将被CasFilter拦截,获取服务端返回的Service Ticket进行登录
shiroFilterFactoryBean.setLoginUrl("https://www.laixiaoming.com:8443/cas/login?service=http://www.localhost1.com:9090/cas");
//登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/");
//未授权跳转页面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
return shiroFilterFactoryBean;
}
@Bean
public MyCasRealm casRealm(){
//使用自定义Realm
MyCasRealm casRealm = new MyCasRealm();
casRealm.setCachingEnabled(true);
casRealm.setAuthenticationCachingEnabled(true);
casRealm.setAuthenticationCacheName("authenticationCache");
casRealm.setAuthorizationCachingEnabled(true);
casRealm.setAuthorizationCacheName("authorizationCache");
//指定CAS服务端地址
casRealm.setCasServerUrlPrefix("https://www.laixiaoming.com:8443/cas");
//当前应用的CAS服务URL,用于接收和处理CAS服务端的Ticket
casRealm.setCasService("http://www.localhost1.com:9090/cas");
return casRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(casRealm());
return securityManager;
}
/**
* Shiro生命周期处理器
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启Shiro AOP的注解支持(如@RequiresRoles,@RequiresPermissions)
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
3 单点登出配置
退出登录时,将页面重定向到CAS的退出页面,service参数的设置指定了退出登录后的回调地址:
@GetMapping("/logout")
public String logout(){
SecurityUtils.getSubject().logout();
return "redirect:https://www.laixiaoming.com:8443/cas/logout?service=https://www.laixiaoming.com:9090/logouttips";
}
service参数名可以在CAS服务端通过修改application.properties进行配置,关于登出的更多配置,可见官网:
#单点登出
#配置允许登出后跳转到指定页面
cas.logout.followServiceRedirects=true
#跳转到指定页面需要的参数名为 service
cas.logout.redirectParameter=service
在CAS服务端退出后,会向注册的每个服务发送登出请求,该请求可以由SingleSignOutFilter进行拦截并销毁当前会话:
@Configuration
public class CasConfig {
@Bean
public FilterRegistrationBean singleSignOutFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean(new SingleSignOutFilter());
registration.addUrlPatterns("/*");
return registration;
}
@Bean
public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutListenerRegistration() {
ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> registration =
new ServletListenerRegistrationBean<>(new SingleSignOutHttpSessionListener());
return registration;
}
}
4 CAS服务端配置
客户端搭建好后,需要在CAS服务端上配置哪些服务可以注册到CAS服务端上,服务的管理也有许多方式,这里使用的JSON的方式,需要先在服务端引入相关依赖(更多说明见官网):
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-json-service-registry</artifactId>
<version>${cas.version}</version>
</dependency>
然后,在已经生成的war包中找到文件:
将文件复制到这个位置:
修改,serviceId指定允许注册的客户端,更多配置见官网:
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://(www\\.localhost1\\.com:9090|www\\.localhost2\\.com:9091)/.*",
"name" : "HTTPS and IMAPS",
"id" : 10000001,
"description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder" : 10000
}
同时在application.properties指定配置文件位置:
#客户端服务注册
cas.serviceRegistry.json.location=classpath:/services
至此,CAS客户端搭建完成了,完整代码见:GitHub
参考:
https://blog.csdn.net/qq_34021712/article/category/8004639/1