准备工作主要做两件事。
成都创新互联自2013年创立以来,先为阿巴嘎等服务建站,阿巴嘎等地企业,进行企业商务咨询服务。为阿巴嘎企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
1.1 服务记录
某一个 Client 需要接入 CAS Server 进行验证,则该 Client 必须提前在 CAS Server 上配置其信息。
这个信息既可以动态添加,也可以通过 JSON 来配置,后面松哥会教搭建如何动态添加,这里方便起见,我们还是通过 JSON 来进行配置。
具体配置方式如下,在 CAS Server 中创建如下目录:
- src/main/resources/services
在该目录下创建一个名为 client1-99.json 的文件,client1 表示要接入的 client 的名字,99 表示要接入的 client 的 id,json 文件内容如下(这个配置可以参考官方给出的模版:overlays/org.apereo.cas.cas-server-webapp-tomcat-5.3.14/WEB-INF/classes/services/Apereo-10000002.json):
- {
- "@class": "org.apereo.cas.services.RegexRegisteredService",
- "serviceId": "^(https|http)://.*",
- "name": "client1",
- "id": 99,
- "description": "应用1 的定义信息",
- "evaluationOrder": 1
- }
这段 JSON 配置含义如下:
接下来再在 src/main/resources/application.properties 文件中配置刚刚 json 的信息,如下:
- cas.serviceRegistry.json.location=classpath:/services
- cas.serviceRegistry.initFromJson=true
这里有两行配置:
OK,配置完成后,重启 CAS Server。
CAS Server 启动成功后,我们在控制台看到如下日志,表示 JSON 配置已经加载成功了:
1.2 JDK 证书
第二个要提前准备的东西就是 JDK 证书。
在实际开发中,这一步可以忽略,但是因为我们现在用的自己生成的 SSL 证书,所以我们要将自己生成的证书导入到 JDK 中,否则在使用 Spring Security 接入 CAS 单点登录时,会抛出如下错误:
将 SSL 证书导入 JDK 中的命令其实也很简单,两个步骤,第一个导出 .cer 文件,第二步,导入 JDK,命令如下:
- keytool -export -trustcacerts -alias casserver -file ./cas.cer -keystore ./keystore
- sudo keytool -import -trustcacerts -alias casserver -file ./cas.cer -keystore /Library/Java/JavaVirtualMachines/jdk-10.0.2.jdk/Contents/Home/lib/security/cacerts
注意,在执行 export 导出命令时,需要输入密钥口令,这个口令就是自己一开始创建 SSL 证书时设置的。在执行 import 导入命令时,也需要输入口令,这个口令是 changeit,注意,不是自己一开始设置的。
密钥库的位置在 JDK 目录下的 /lib/security/cacerts,小伙伴们根据自己实际情况来修改(在 JDK9 之前,位置在 jre/lib/security/cacerts)。
我们在本地测试一定要导入证书到 JDK 证书库中,否则后面的测试会出现上图中的错误,证书导入 JDK 证书库之后,要确保之后的开发中,使用的是本地的 JDK。
注意,JDK 证书导入之后,CASServer 需要重启一下。
1.3 修改 hosts
另外,我们还需要修改电脑 hosts 文件,因为前面关于 CAS Server,关于 SSL 证书的配置都涉及到域名,所以后面的访问我们将通过域名的形式访问,hosts 文件中添加如下两条记录:
第一个是 CAS Server 的请求域名,第二个是 CAS Client 的请求域名。
在使用 Spring Security 开发 CAS Client 之前,有一个基本问题需要先和小伙伴们捋清楚:用户登录是在 CAS Server 上登录,所以 Spring Security 中虽然依旧存在用户的概念,但是对于用户的处理逻辑会和前面的有所不同。
好了,接下来我们来看下具体步骤。
首先我们来创建一个普通的 Spring Boot 项目,加入 Web 依赖 和 Spring Security 依赖,如下:
项目创建成功后,我们再来手动加入 cas 依赖:
org.springframework.security spring-security-cas
接下来,在 application.properties 中配置 CAS Server 和 CAS Client 的请求地址信息:
- cas.server.prefix=https://cas.javaboy.org:8443/cas
- cas.server.login=${cas.server.prefix}/login
- cas.server.logout=${cas.server.prefix}/logout
- cas.client.prefix=http://client1.cas.javaboy.org:8080
- cas.client.login=${cas.client.prefix}/login/cas
- cas.client.logoutRelative=/logout/cas
- cas.client.logout=${cas.client.prefix}${cas.client.logoutRelative}
这些配置都是自定义配置,所以配置的 key 可以自己随意定义。至于配置的含义都好理解,分别配置了 CAS Server 和 CAS Client 的登录和注销地址。
配置好之后,我们需要将这些配置注入到实体类中使用,这里就用到了类型安全的属性绑定。
这里我创建两个类分别用来接收 CAS Server 和 CAS Client 的配置文件:
- @ConfigurationProperties(prefix = "cas.server")
- public class CASServerProperties {
- private String prefix;
- private String login;
- private String logout;
- //省略 getter/setter
- }
- @ConfigurationProperties(prefix = "cas.client")
- public class CASClientProperties {
- private String prefix;
- private String login;
- private String logoutRelative;
- private String logout;
- //省略 getter/setter
- }
另外记得在启动类上面添加 @ConfigurationPropertiesScan 注解来扫描这两个配置类:
- @SpringBootApplication
- @ConfigurationPropertiesScan
- public class Client1Application {
- public static void main(String[] args) {
- SpringApplication.run(Client1Application.class, args);
- }
- }
这里配置完成后,我们一会将在配置文件中来使用。
接下来创建 CAS 的配置文件,略长:
- @Configuration
- public class CasSecurityConfig {
- @Autowired
- CASClientProperties casClientProperties;
- @Autowired
- CASServerProperties casServerProperties;
- @Autowired
- UserDetailsService userDetailService;
- @Bean
- ServiceProperties serviceProperties() {
- ServiceProperties serviceProperties = new ServiceProperties();
- serviceProperties.setService(casClientProperties.getLogin());
- return serviceProperties;
- }
- @Bean
- @Primary
- AuthenticationEntryPoint authenticationEntryPoint() {
- CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
- entryPoint.setLoginUrl(casServerProperties.getLogin());
- entryPoint.setServiceProperties(serviceProperties());
- return entryPoint;
- }
- @Bean
- TicketValidator ticketValidator() {
- return new Cas20ProxyTicketValidator(casServerProperties.getPrefix());
- }
- @Bean
- CasAuthenticationProvider casAuthenticationProvider() {
- CasAuthenticationProvider provider = new CasAuthenticationProvider();
- provider.setServiceProperties(serviceProperties());
- provider.setTicketValidator(ticketValidator());
- provider.setUserDetailsService(userDetailService);
- provider.setKey("javaboy");
- return provider;
- }
- @Bean
- CasAuthenticationFilter casAuthenticationFilter(AuthenticationProvider authenticationProvider) {
- CasAuthenticationFilter filter = new CasAuthenticationFilter();
- filter.setServiceProperties(serviceProperties());
- filter.setAuthenticationManager(new ProviderManager(authenticationProvider));
- return filter;
- }
- @Bean
- SingleSignOutFilter singleSignOutFilter() {
- SingleSignOutFilter sign = new SingleSignOutFilter();
- sign.setIgnoreInitConfiguration(true);
- return sign;
- }
- @Bean
- LogoutFilter logoutFilter() {
- LogoutFilter filter = new LogoutFilter(casServerProperties.getLogout(), new SecurityContextLogoutHandler());
- filter.setFilterProcessesUrl(casClientProperties.getLogoutRelative());
- return filter;
- }
- }
这个配置文件略长,但是并不难,我来和大家挨个解释:
接下来我再来给大家看下我定义的 UserDetailsService:
- @Component
- @Primary
- public class UserDetailsServiceImpl implements UserDetailsService{
- @Override
- public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
- return new User(s, "123", true, true, true, true,
- AuthorityUtils.createAuthorityList("ROLE_user"));
- }
- }
既然是单点登录,也就是用户是在 CAS Server 上登录的,这里的 UserDetailsService 意义在哪里呢?
用户虽然在 CAS Server 上登录,但是,登录成功之后,CAS Client 还是要获取用户的基本信息、角色等,以便做进一步的权限控制,所以,这里的 loadUserByUsername 方法中的参数,实际上就是你从 CAS Server 上登录成功后获取到的用户名,拿着这个用户名,去数据库中查询用户的相关信心并返回,方便 CAS Client 在后续的鉴权中做进一步的使用,这里我为了方便,就没有去数据库中查询了,而是直接创建了一个 User 对象返回。
接下来,我们再来看看 Spring Security 的配置:
- @Configuration
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- @Autowired
- AuthenticationEntryPoint authenticationEntryPoint;
- @Autowired
- AuthenticationProvider authenticationProvider;
- @Autowired
- SingleSignOutFilter singleSignOutFilter;
- @Autowired
- LogoutFilter logoutFilter;
- @Autowired
- CasAuthenticationFilter casAuthenticationFilter;
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.authenticationProvider(authenticationProvider);
- }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests().antMatchers("/user/**")
- .hasRole("user")
- .antMatchers("/login/cas").permitAll()
- .anyRequest().authenticated()
- .and()
- .exceptionHandling()
- .authenticationEntryPoint(authenticationEntryPoint)
- .and()
- .addFilter(casAuthenticationFilter)
- .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
- .addFilterBefore(logoutFilter, LogoutFilter.class);
- }
- }
这里的配置就简单很多了:
最后,再提供两个测试接口:
- @RestController
- public class HelloController {
- @GetMapping("/hello")
- public String hello() {
- return "hello";
- }
- @GetMapping("/user/hello")
- public String user() {
- return "user";
- }
- }
OK ,如此之后,我们的 CAS Client 现在就开发完成了,接下来启动 CAS Client,启动成功后,浏览器输入 http://client1.cas.javaboy.org:8080/user/hello 访问 hello 接口,此时会自动跳转到 CAS Server 上登录,登录成功之后,经过两个重定向,会重新回到 hello 接口。
OK,这就是松哥和大家介绍的 Spring Security + CAS 单点登录,当然,这个案例中还有很多需要完善的地方,松哥会在后面的文章中继续和大家分享完善的方案。
好了 ,本文就说到这里,本文相关案例我已经上传到 GitHub ,大家可以自行下载:https://github.com/lenve/spring-security-samples
本文转载自微信公众号「江南一点雨」,可以通过以下二维码关注。转载本文请联系江南一点雨公众号。
网页名称:SpringSecurity系列之SpringBoot+CAS单点登录
文章源于:http://www.csdahua.cn/qtweb/news17/81417.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网