springboot+springSecurity+springSessionDataRedis+CAS搭建集群单点登陆系统
2016-11-21 17:39
543 查看
springboot+springSecurity+springSessionDataRedis+CAS搭建集群单点登陆系统
环境描述
windows 7 64bit spring boot:1.4.0.RELEASE spring security:4.1.0.RELEASE CAS:4.1.3 nginx:随便
搭建CAS服务
1.Tomcat配置SSL
1.生成证书以命令方式进入目录%TOMCAT_HOME%,输入如下命令:
keytool -genkey -alias tomcat_key -keyalg RSA -storepass changeit -keystore server.keystore -validity 3600
注意: -storepass 与 -validity 可以根据需要更改
用户名输入域名,如:test.cas.com,其它全部以Enter跳过,最后确认,此时会在%TOMCAT_HOME%下生成server.keystore文件
2.将证书导入到客户端使用的JDK的证书信任库中
这步对于Tomcat的SSL配置不是必须,但对于CAS SSO是必须的,否则会出现如下错误:
edu.yale.its.tp.cas.client.CASAuthenticationException: Unable to validate ProxyTicketValidate.
导入过程分2步,第一步是导出证书,第二步是导入到证书信任库,命令如下:
keytool -export -trustcacerts -alias tomcat_key -file server.cer -keystore server.keystore -storepass changeit keytool -import -trustcacerts -alias tomcat_key -file server.cer -keystore D:/”Program Files”/Java/jdk1.8.0_60/jre/lib/security/cacerts -storepass changeit
其它有用keytool命令(列出信任证书库中所有已有证书,删除库中某个证书):
keytool -list -keystore %JAVA_HOME%/jre/lib/security/cacerts >t.txt keytool -delete -trustcacerts -alias tomcat_key -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
3.tomcat启用ssl
配置server.xml:
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="9443" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" keystoreFile="server.keystore" keystorePass="changeit" clientAuth="false" sslProtocol="TLS"/>
2.CAS服务的配置
1.部署CAS web server:从https://github.com/apereo 下载CAS server对应的版本源码
解压,使用maven命令mvn package对cas进行编译打包,这里可能会出现2个问题:
a.单元测试不通过==》尝试加上-Dmaven.test.skip=true跳过;
b.编译到一半编译不过==》进入cas-server-core,执行命令mvn install -Dmaven.test.skip=true先将core编译为jar包后,再进行。
3.将打包好的cas-server-webapp下的war包复制到tomcat下,运行tomcat先试试吧;
注意:如果觉得麻烦,可以上网搜索cas-server-webapp,使用别人打好的war包
4.将war包放入tomcat的webapps目录下即可
2.从数据库获取用户信息
1.修改%tomcathome%\webapps\cas\WEB-INF中deployerConfigContext.xml:
<constructor-arg> <map> <!-- | IMPORTANT | Every handler requires a unique name. | If more than one instance of the same handler class is configured, you must explicitly | set its name to something other than its default name (typically the simple class name). --> <!--<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />--> <!--<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />--> <entry key-ref="dbAuthHandler" value-ref="primaryPrincipalResolver"/> </map> </constructor-arg> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://127.0.0.1:3306/demo2?characterEncoding=utf8" p:user="root" p:password="system" /> <bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" c:encodingAlgorithm="MD5" p:characterEncoding="UTF-8" /> <bean id="dbAuthHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler" p:dataSource-ref="dataSource" p:sql="select password_hash from jhi_user where login = ?" p:passwordEncoder-ref="passwordEncoder"/>
注意:配置中使用mysql数据库,c3p0连接池,需要引入相关的jar包
将c3p0-0.9.1.1.jar,cas-server-support-jdbc-4.1.3.jar,mysql-connector-java-5.1.32.jar放入%tomcathome%\webapps\cas\WEB-INF\lib中
2.修改%tomcathome%\webapps\cas\WEB-INF\classes\services\Apereo-10000002.json
"serviceId" : "^http.*",
3.设置ST(service ticket)过期策略
修改%tomcathome%\webapps\cas\WEB-INF\spring-configuration\ticketExpirationPolicies.xml
<bean id="serviceTicketExpirationPolicy" class="org.jasig.cas.ticket.support.MultiTimeUseOrTimeoutExpirationPolicy" c:numberOfUses="10" c:timeToKill="${st.timeToKillInSeconds:1000}" c:timeUnit-ref="SECONDS"/>
注意:根据需要进行修改
至此,所需配置完成,浏览器:http://test.cas.com/cas,进行登陆测试!
注意:test.cas.com为CAS 服务所在机器的域名(如无进行域名配置,可以修改本机hosts,127.0.0.1 test.cas.com达到测试效果)
整合springSecurity与CAS
1.在springboot项目中引入依赖:
<!-- security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-data</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-cas</artifactId> </dependency>
2.配置类
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Inject public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(casAuthenticationProvider()); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers(HttpMethod.OPTIONS, "/**") .antMatchers("/app/**/*.{js,html}") .antMatchers("/bower_components/**") .antMatchers("/i18n/**") .antMatchers("/content/**") .antMatchers("/test/**") ; } @Bean public PasswordEncoder passwordEncoder() { return new StandardPasswordEncoder("tnappsk1234"); } @Bean public ServiceProperties serviceProperties() { ServiceProperties sp = new ServiceProperties(); //从cas server获取st后重定向的url,casAuthenticationFilter进行捕获,默认以/login/cas结尾 String fstr = "http://test.tn.com/login/cas"; sp.setService(fstr); sp.setSendRenew(false); return sp; } @Bean public CasAuthenticationEntryPoint casAuthenticationEntryPoint(){ CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); ep.setServiceProperties(serviceProperties()); //cas server 登陆路径 ep.setLoginUrl("https://test.cas.com:9443/cas/login"); return ep; } @Bean public CasAuthenticationProvider casAuthenticationProvider(){ CasAuthenticationProvider pro = new CasAuthenticationProvider(); pro.setServiceProperties(serviceProperties()); pro.setAuthenticationUserDetailsService(customUserDetailsService()); pro.setTicketValidator(cas20ServiceTicketValidator()); pro.setKey("an_id_for_this_auth_provider_only"); return pro; } @Bean public Cas20ServiceTicketValidator cas20ServiceTicketValidator() { //cas server路径 return new Cas20ServiceTicketValidator("https://test.cas.com:9443/cas"); } @Bean public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() { Set<String> admins = new HashSet<String>(); String adminUserName = "admin"; admins.add("admin"); if (adminUserName != null && !adminUserName.isEmpty()) { admins.add(adminUserName); } return new CustomUserDetailsService(admins); } @Bean public CasAuthenticationFilter casAuthenticationFilter() throws Exception{ CasAuthenticationFilter af = new CasAuthenticationFilter(); af.setSessionAuthenticationStrategy(sessionStrategy()); af.setAuthenticationManager(authenticationManager()); return af; } @Bean public SessionAuthenticationStrategy sessionStrategy() { SessionAuthenticationStrategy sessionStrategy = new SessionFixationProtectionStrategy(); return sessionStrategy; } @Bean public SingleSignOutFilter singleSignOutFilter() { SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter(); singleSignOutFilter.setIgnoreInitConfiguration(true); singleSignOutFilter.setCasServerUrlPrefix("https://test.cas.com:9443/cas"); return singleSignOutFilter; } @Bean public LogoutFilter requestCasGlobalLogoutFilter() { b06d LogoutFilter logoutFilter = new LogoutFilter("https://test.cas.com:9443/cas/logout?service=http://test.tn.com", new SecurityContextLogoutHandler()); logoutFilter.setFilterProcessesUrl("/api/logout"); logoutFilter.setLogoutRequestMatcher(new AntPathRequestMatcher("/api/logout", "POST")); return logoutFilter; } @Override protected void configure(HttpSecurity http) throws Exception { http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint()) .and().addFilter(casAuthenticationFilter()) .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class) .addFilterBefore(requestCasGlobalLogoutFilter(), LogoutFilter.class); http .csrf().disable() .headers() .frameOptions() .disable() .and() .authorizeRequests() .antMatchers("/api/register").denyAll() .antMatchers("/api/activate").permitAll() .antMatchers("/api/content-items/category").permitAll() .antMatchers("/api/authenticate").permitAll() .antMatchers("/api/account/reset_password/init").permitAll() .antMatchers("/api/account/reset_password/finish").permitAll() .antMatchers("/api/profile-info").permitAll() .antMatchers("/api/**").authenticated() .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) .antMatchers("/v2/api-docs/**").permitAll() .antMatchers("/swagger-resources/configuration/ui").permitAll() .antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN); } @Bean public SecurityEvaluationContextExtension securityEvaluationContextExtension() { return new SecurityEvaluationContextExtension(); } }
注意:密码passwordEncoder(),需要配置成与cas一致,使用MD5,这里配置有误
主要:CasAuthenticationFilter,CasAuthenticationEntryPoint,CasAuthenticationProvider的配置,
具体可以参考Spring Security官方文档CAS Authentication部分:
http://docs.spring.io/spring-security/site/docs/4.2.0.RELEASE/reference/htmlsingle/#cas
使用redis做http session共享
使用redis做http session的缓存,这样当从cas server 重定向回某类应用的service url(下有多个机子组成集群)时,可以指定同一个域名,这样session就实现了共享。1.引入依赖
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
2.配置类
@Configuration @EnableRedisHttpSession public class RedisSessionConfig { /** * 修改数据在redis中的存储方式,方便调试 * @Title: sessionRedisTemplate * @Description: TODO * @param connectionFactory * @return * @return: RedisTemplate<Object,Object> */ @Bean public RedisTemplate<Object, Object> sessionRedisTemplate( RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>(); template.setKeySerializer(new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(connectionFactory); return template; } }
使用nginx做负载均衡
修改nginx.conf,负载均衡采用轮询的策略:
http{ upstream tn { server test.tn.com:6900; server test.tn.com:6901; } server{ listen 80; location / { proxy_pass http://tn; } }
修改hosts
127.0.0.1 test.tn.com //webapp 127.0.0.1 test.cas.com //cas server
start nginx启动
浏览器中,访问http://test.tn.com/xxx/xxx(受管控方法),跳转到Cas Server,登陆验证身份后,重定向回之前的url,不断刷新,通过观察日子,请求分发到不同的webapp
相关文章推荐
- springboot+springSecurity+springSessionDataRedis+CAS搭建集群单点登陆系统
- 搭建nginx+tomcat+memcache三套集群,开发简单springboot工程,实现session共享(持续更新中)
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- springboot +jpa + security 博客系统
- docker搭建nginx+springboot集群
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Sping Boot + Spring Security + Mybaits + Logback +JWT验证 项目开发框架搭建
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- 整合了Spring-Security的业务系统无法通过CAS进行单点注销
- spring mvc+shiro +cas +spring-session 的通用权限管理系统
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- Spring Boot+JPA+Mysql+ThymeLeaf快速构建CURD系统(二)搭建SpringBoot工程
- spirng-boot中,基于既有的token验证方式,利用spring-security实现权限系统
- Spring Boot 搭建应用实现登陆实例,页面使用bootstrap