ticket 过期策略配置
<!-- TGT 配置--> <bean id="serviceTicketExpirationPolicy" class="org.jasig.cas.ticket.support.MultiTimeUseOrTimeoutExpirationPolicy"> <!-- This argument is the number of times that a ticket can be used before its considered expired. (ticket 在过期前[下 过期时间 参数] 可以使用的次数) --> <constructor-arg index="0" value="999999999" /> <!-- This argument is the time a ticket can exist before its considered expired. ( 过期时间 单位:毫秒) --> <constructor-arg index="1" value="100000" /> </bean> <!--ST 配置 --> <bean id="grantingTicketExpirationPolicy" class="org.jasig.cas.ticket.support.TimeoutExpirationPolicy"> <!-- This argument is the time a ticket can exist before its considered expired. (ticket 在不活跃的时候过期时间 毫秒) --> <constructor-arg index="0" value="7200000" /> </bean>
CAS 会将生成的 TGT 放在 session 中, 而 TGC 就是这个 session 的唯一标识(sessionId), 可以认为是 TGT 的key, 为 TGT 就是 TGC 的 value, TGC 以 cookie 的形式保存在浏览器中, 每个请求都会尝试携带 TGC; (每个服务都会在 session 和 cookie 中保存对应的 TGT 和 TGC)
TGT 是CAS 为用户签发的登录 ticket, 也是用于验证用户登录成功的唯一方式; TGT 封装了 Cookie 值以及 Cookie 值对应的用户信息, CAS 通过 Cookie 值(TGC)为 key 查询缓存中有无 TGT(TGC:TGT(key:value)), 如果有的话就说明用户已经登录成;
ST 是当用户访问某一服务时提供的 ticket; 用户在访问其他服务时, 发现没有 cookie 或 ST , 那么就会302到 CAS 服务器获取 ST; 然后会携带着 ST 302 回来;
CasAuthenticationEntryPoint;<!-- 指定登录入口为casEntryPoint --> <security:http entry-point-ref="casEntryPoint"> ... </security:http>
CasAuthenticationEntryPoint对应的bean, 然后指定需要进行登录认证时使用该
AuthenticationEntryPoint;<!-- 认证的入口 --> <bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"> <!-- Cas Server的登录地址, elim是我的计算机名 --> <property name="loginUrl" value="https://elim:8443/cas/login" /> <!-- service相关的属性 --> <property name="serviceProperties" ref="serviceProperties" /> </bean>
ServiceProperties, 该对象主要用来描述service(Cas概念)相关的属性, 主要是指定在Cas Server认证成功后将要跳转的地址;<!-- 指定service相关信息 --> <bean id="serviceProperties"class="org.springframework.security.cas.ServiceProperties"> <!-- Cas Server认证成功后的跳转地址, 这里要跳转到我们的Spring Security应用, 之后会由CasAuthenticationFilter处理, 默认处理地址为/j_spring_cas_security_check --> //<!>本地地址了, 这里开始就是本地Spring Security 流程了, 再由 cas filter 封装 UsernamePasswordAuthenticationToken //再到 AuthenticationManager, 到 AuthenticationProvider 流程.. <property name="service" value="http://elim:8080/app/j_spring_cas_security_check" /> </bean>
CasAuthenticationFilter, 并将其放置在Filter链表中CAS_FILTER的位置, 以处理Cas Server认证成功后的页面跳转, 用以在Spring Security中进行认证;该Filter会将Cas Server传递过来的ticket(Cas概念)封装成一个
Authentication(对应UsernamePasswordAuthenticationToken, 其中ticket作为该Authentication的password), 然后传递给AuthenticationManager进行认证;
<security:http entry-point-ref="casEntryPoint"> ... <!-- 定义CasAuthenticationFilter 并将其放置在Filter链表中CAS_FILTER的位置--> <security:custom-filter ref="casFilter" position="CAS_FILTER"/> ... </security:http> <bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter"> <property name="authenticationManager" ref="authenticationManager" /> <!-- 指定处理地址, 不指定时默认将会是"/j_spring_cas_security_check" --> <property name="filterProcessesUrl" value="/j_spring_cas_security_check"/> </bean>
CasAuthenticationFilter会将封装好的包含Cas
Server传递过来的ticket的Authentication对象传递给
AuthenticationManager进行认证;<security:authentication-manager alias="authenticationManager"><!-- 给AuthenticationManager 提供一个 认证 ; 参考: 细谈认证过程 5--> <security:authentication-provider ref="casAuthenticationProvider"/> </security:authentication-manager>
所以接下来我们要在AuthenticationManager中配置一个能够处理
CasAuthenticationFilter传递过来的Authentication对象的AuthenticationProvider实现,
CasAuthenticationProvider;
<!> 真正认证逻辑bean <bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"> <!-- 通过username来加载UserDetails --> <property name="authenticationUserDetailsService"> <beanclass="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <!-- 真正加载UserDetails的UserDetailsService实现 --> <constructor-arg ref="userDetailsService" /> </bean> </property> <property name="serviceProperties" ref="serviceProperties" /> <!-- 配置TicketValidator在登录认证成功后验证ticket --> <property name="ticketValidator"> <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"> <!-- Cas Server访问地址的前缀, 即根路径--> <constructor-arg index="0" value="https:// elim:8443/cas" /> </bean> </property> <property name="key" value="key4CasAuthenticationProvider" /> </bean>
CasAuthenticationProvider首先会利用TicketValidator(Cas概念)对Authentication中包含的ticket信息进行认证;
认证通过后将利用持有的
AuthenticationUserDetailsService 根据认证通过后回传的Assertion对象中拥有的username加载用户对应的UserDetails,
即主要是加载用户的相关权限信息GrantedAuthority; 然后构造一个 CasAuthenticationToken进行返回; 之后的逻辑就是正常的Spring Security的逻辑了;
访问测试
http://127.0.0.1/cas-server/login
https://127.0.0.1:8443/cas-server/login
<!-- security 对CAS支持 --><dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-cas</artifactId></dependency>
//入口点
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() { CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(casProperties.getServerPrefix()+casProperties.getServerLogin());
casAuthenticationEntryPoint.setServiceProperties(serviceProperties()); return casAuthenticationEntryPoint;
} // 本地缓存认证
@Bean
public CasLocalCacheAuthenticationFilter casLocalCacheAuthenticationFilter() {
CasLocalCacheAuthenticationFilter ccaf; try {
ccaf = new CasLocalCacheAuthenticationFilter(casProperties, redisTemplate, casUserDetailsService());
} catch (Exception e) {
e.printStackTrace(); throw new RuntimeException(e.getMessage() );
} return ccaf;
} // cas 认证过滤器
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception { CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLogin());
casAuthenticationFilter.setAuthenticationSuccessHandler(casAuthenticationSuccessHandler()); return casAuthenticationFilter;
} ////////////认证相关处理
@Bean
@Override
protected AuthenticationManager authenticationManager() {
List<AuthenticationProvider> p = new ArrayList<AuthenticationProvider>(1);
p.add(casAuthenticationProvider()); ProviderManager authenticationManager = new ProviderManager(p); return authenticationManager;
} @Bean
public CasAuthenticationProvider casAuthenticationProvider() { CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider(); try {
casAuthenticationProvider.setAuthenticationUserDetailsService(casUserDetailsService());
} catch (Exception e) {
e.printStackTrace(); throw new RuntimeException(e.getMessage() );
}
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("jk_www_123ProviderKey"); return casAuthenticationProvider;
} @Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() { return new Cas20ServiceTicketValidator(casProperties.getServerPrefix());
}
@Bean
public ServiceProperties serviceProperties(soccer.soccer.zuq8.info) { ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(casProperties.getAppPrefix()+ casProperties.getAppLogin());
serviceProperties.setAuthenticateAllArtifacts(true); return serviceProperties;
} @Bean
public CasAuthenticationSuccessHandler casAuthenticationSuccessHandler() { CasAuthenticationSuccessHandler casAuthenticationSuccessHandler = new CasAuthenticationSuccessHandler(this.casProperties, redisTemplate); return casAuthenticationSuccessHandler;
}
// 本地 UserDetailsService
@Bean
@ConditionalOnMissingBean( value={AuthenticationUserDetailsService.class} )
public AuthenticationUserDetailsService casUserDetailsService() throws Exception { return new CasUserDetailsServiceImp();
} //登出
@Bean
public CasLogoutFilter casLogoutFilter(gov.cb.zuzb.info) { return new CasLogoutFilter(casProperties, redisTemplate);
}
//配置
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
log.info("当前使用安全配置是: cas");
ExpressionInterceptUrlRegistry eiur=httpSecurity // 禁用 CSRF
.csrf().disable() // 授权异常
.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint() ).and() // 不创建会话
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //
httpSecurity
.formLogin()
.loginProcessingUrl(casProperties.getAppLogin()); // 防止iframe 造成跨域
.and().headers().frameOptions().disable();
httpSecurity
.addFilterAt(casLogoutFilter(), LogoutFilter.class)
.addFilterBefore(casAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(casLocalCacheAuthenticationFilter(), CasAuthenticationFilter.class);
}
定义一个 CasProperties 配置类,用于将properties文件指定的内容注入以方便使用
public class CasProperties { private String serverPrefix; private String serverLogin; private String serverLogout; //
private String appPrefix; private String appLogin; private String appLogout;
CasAuthenticationFilter 认证过滤器
@Beanpublic CasAuthenticationFilter casAuthenticationFilter() throws Exception { CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter(); //根据 spring securiry 步骤 authenticationManager 会调用 Provider list 认证 规则
//所以还要把CasAuthenticationProvider 注给 authenticationManager, 最好覆盖父的方法, 自己写.. 先不管了
List<AuthenticationProvider> p = new ArrayList<AuthenticationProvider>(1);
p.add(casAuthenticationProvider(gov.zuqiu.yc.jrzb1.info)); ProviderManager authenticationManager = new ProviderManager(p);
casAuthenticationFilter.setAuthenticationManager(authenticationManager);
casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLogin());//cas 的回调 登陆地址
return casAuthenticationFilter;
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3107305/ ,如需转载,请注明出处,否则将追究法律责任。