diff --git a/integrations/spring-security-cas-demo/pom.xml b/integrations/spring-security-cas-demo/pom.xml new file mode 100644 index 00000000..4c9d9f7b --- /dev/null +++ b/integrations/spring-security-cas-demo/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.unicom.sso + sso-springboot-sample + 0.0.1-SNAPSHOT + + + sso-bigdata-cas-starter-demo + cas 单点登录demo + + + 8 + 8 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.security + spring-security-cas + + + + \ No newline at end of file diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/CasApplication.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/CasApplication.java new file mode 100644 index 00000000..aa0b81ae --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/CasApplication.java @@ -0,0 +1,17 @@ +package com.unicom.sso.bigdata.cas.demo; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * cas 单点 + * @author baihz10 + * @date 2023/7/7 15:18 + */ +//@EnableCasClient +@SpringBootApplication +public class CasApplication { + public static void main(String[] args) { + SpringApplication.run(CasApplication.class, args); + } +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/AuthorityInfo.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/AuthorityInfo.java new file mode 100644 index 00000000..60608e80 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/AuthorityInfo.java @@ -0,0 +1,25 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import org.springframework.security.core.GrantedAuthority; + +public class AuthorityInfo implements GrantedAuthority { + private static final long serialVersionUID = -175781100474818800L; + + /** + * 权限CODE + */ + private String authority; + + public AuthorityInfo(String authority) { + this.authority = authority; + } + + @Override + public String getAuthority() { + return authority; + } + + public void setAuthority(String authority) { + this.authority = authority; + } +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CasConfig.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CasConfig.java new file mode 100644 index 00000000..067150ef --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CasConfig.java @@ -0,0 +1,120 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import org.jasig.cas.client.session.SingleSignOutFilter; +import org.jasig.cas.client.validation.Cas20ServiceTicketValidator; +import org.jasig.cas.client.validation.Cas30ProxyTicketValidator; +import org.jasig.cas.client.validation.Cas30ServiceTicketValidator; +import org.jasig.cas.client.validation.TicketValidator; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationEntryPoint; +import org.springframework.security.cas.web.CasAuthenticationFilter; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Configuration +public class CasConfig { + + @Value("${cas.server.url}") + private String casServerUrl; + @Value("${base.url}") + private String baseUrl; + + @Bean + public AuthenticationEntryPoint authenticationEntryPoint() { + CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint(); + entryPoint.setLoginUrl(casServerUrl + "/login"); + entryPoint.setServiceProperties(this.serviceProperties()); + return entryPoint; + } + + @Bean + protected AuthenticationManager authenticationManager() throws Exception { + return new ProviderManager(this.casAuthenticationProvider()); + } + + @Bean + public CasAuthenticationFilter casAuthenticationFilter() throws Exception { + CasAuthenticationFilter filter = new CasAuthenticationFilter(); + filter.setAuthenticationManager(this.authenticationManager()); + filter.setServiceProperties(this.serviceProperties()); + filter.setAuthenticationFailureHandler(this.authenticationFailureHandler()); + filter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler()); + return filter; + } + + @Bean + public AuthenticationSuccessHandler authenticationSuccessHandler() { + AuthenticationSuccessHandler authenticationSuccessHandler = new SimpleUrlAuthenticationSuccessHandler(); + + return authenticationSuccessHandler; + } + + @Bean + public AuthenticationFailureHandler authenticationFailureHandler() { + AuthenticationFailureHandler authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(); + + return authenticationFailureHandler; + } + + @Bean + public ServiceProperties serviceProperties() { + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setService(baseUrl); + serviceProperties.setSendRenew(false); + return serviceProperties; + } + + @Bean + public TicketValidator ticketValidator() { + return new Cas30ProxyTicketValidator(casServerUrl); + } + + @Bean + public CasAuthenticationProvider casAuthenticationProvider() { + CasAuthenticationProvider provider = new CasAuthenticationProvider(); + provider.setServiceProperties(this.serviceProperties()); + provider.setTicketValidator(this.ticketValidator()); + provider.setAuthenticationUserDetailsService(new UserDetailsServiceImpl()); + provider.setKey("CAS_PROVIDER_LOCALHOST"); + return provider; + } + + @Bean + public SecurityContextLogoutHandler securityContextLogoutHandler() { + return new SecurityContextLogoutHandler(); + } + + @Bean + public LogoutFilter logoutFilter() { + LogoutFilter logoutFilter = new LogoutFilter(casServerUrl + "/logout?service=" + baseUrl, + securityContextLogoutHandler()); + logoutFilter.setFilterProcessesUrl("/logout/cas"); + return logoutFilter; + } + + @Bean + public SingleSignOutFilter singleSignOutFilter() { + SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter(); + singleSignOutFilter.setIgnoreInitConfiguration(true); + return singleSignOutFilter; + } + +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CasProperties.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CasProperties.java new file mode 100644 index 00000000..15d486ef --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CasProperties.java @@ -0,0 +1,96 @@ +package com.unicom.sso.bigdata.cas.demo.config; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * CAS的配置参数 + */ +@Component +public class CasProperties { + @Value("${cas.server.host.url}") + private String casServerUrl; + + @Value("${cas.server.host.login_url}") + private String casServerLoginUrl; + + @Value("${cas.server.host.logout_url}") + private String casServerLogoutUrl; + + @Value("${app.casEnable}") + private boolean casEnable; + + @Value("${app.server.host.url}") + private String appServerUrl; + + @Value("${app.login_url}") + private String appLoginUrl; + + @Value("${app.logout_url}") + private String appLogoutUrl; + + @Value("${app.web_url}") + private String webUrl; + + public String getWebUrl() { + return webUrl; + } + + public String getCasServerUrl() { + return casServerUrl; + } + + public void setCasServerUrl(String casServerUrl) { + this.casServerUrl = casServerUrl; + } + + public String getCasServerLoginUrl() { + return casServerLoginUrl; + } + + public void setCasServerLoginUrl(String casServerLoginUrl) { + this.casServerLoginUrl = casServerLoginUrl; + } + + public String getCasServerLogoutUrl() { + return casServerLogoutUrl; + } + + public void setCasServerLogoutUrl(String casServerLogoutUrl) { + this.casServerLogoutUrl = casServerLogoutUrl; + } + + public boolean isCasEnable() { + return casEnable; + } + + public void setCasEnable(boolean casEnable) { + this.casEnable = casEnable; + } + + public String getAppServerUrl() { + return appServerUrl; + } + + public void setAppServerUrl(String appServerUrl) { + this.appServerUrl = appServerUrl; + } + + public String getAppLoginUrl() { + return appLoginUrl; + } + + public void setAppLoginUrl(String appLoginUrl) { + this.appLoginUrl = appLoginUrl; + } + + public String getAppLogoutUrl() { + return appLogoutUrl; + } + + public void setAppLogoutUrl(String appLogoutUrl) { + this.appLogoutUrl = appLogoutUrl; + } + +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CustomUserDetailsService.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CustomUserDetailsService.java new file mode 100644 index 00000000..d9f848ad --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/CustomUserDetailsService.java @@ -0,0 +1,17 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public class CustomUserDetailsService implements UserDetailsService { + + @Override + public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { + // 可自定义获取用户信息 + return new User("admin", "admin", true, true, true, true, + AuthorityUtils.createAuthorityList("ROLE_ADMIN")); + } +} \ No newline at end of file diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/JwtAuthenticationTokenFilter.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/JwtAuthenticationTokenFilter.java new file mode 100644 index 00000000..3ab2cd74 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/JwtAuthenticationTokenFilter.java @@ -0,0 +1,37 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} + diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/LoginUser.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/LoginUser.java new file mode 100644 index 00000000..8b760f0c --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/LoginUser.java @@ -0,0 +1,233 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public LoginUser() + { + } + + + /** + * 账户是否未过期,过期无法验证 + */ + @JsonIgnore + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JsonIgnore + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + + @Override + public Collection getAuthorities() + { + return new HashSet(); + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return null; + } +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/SecurityConfig.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/SecurityConfig.java new file mode 100644 index 00000000..d6aad444 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/SecurityConfig.java @@ -0,0 +1,251 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import org.jasig.cas.client.session.SingleSignOutFilter; +import org.jasig.cas.client.validation.Cas20ServiceTicketValidator; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationEntryPoint; +import org.springframework.security.cas.web.CasAuthenticationFilter; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.web.filter.CorsFilter; + +import javax.annotation.Resource; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + @Autowired + private CasProperties casProperties; + + @Autowired + private CasUserDetailsService customUserDetailsService; + + @Autowired + private CasAuthenticationSuccessHandler casAuthenticationSuccessHandler; + + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + //开启cas + if (casProperties.isCasEnable()) { + httpSecurity + // CSRF禁用,因为不使用session + .csrf().disable() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 验证码captchaImage 允许匿名访问 + //.antMatchers("/login", "/captchaImage").anonymous() + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js" + ).permitAll() + .antMatchers("/profile/**").anonymous() + .antMatchers("/common/download**").anonymous() + .antMatchers("/common/download/resource**").anonymous() + .antMatchers("/swagger-ui.html").anonymous() + .antMatchers("/swagger-resources/**").anonymous() + .antMatchers("/webjars/**").anonymous() + .antMatchers("/*/api-docs").anonymous() + .antMatchers("/druid/**").anonymous() + .antMatchers("/websocket/**").anonymous() + .antMatchers("/magic/web/**").anonymous() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable(); + //单点登录登出 + httpSecurity.logout().permitAll().logoutSuccessHandler(logoutSuccessHandler); + // Custom JWT based security filter + httpSecurity.addFilter(casAuthenticationFilter()) + .addFilterBefore(authenticationTokenFilter, CasAuthenticationFilter.class) + //.addFilterBefore(casLogoutFilter(), LogoutFilter.class) + .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class).exceptionHandling() + //认证失败 + .authenticationEntryPoint(casAuthenticationEntryPoint()); + + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + // disable page caching + httpSecurity.headers().cacheControl(); + } + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // cas + if (casProperties.isCasEnable()) { + super.configure(auth); + auth.authenticationProvider(casAuthenticationProvider()); + } + } + + + /** + * 认证的入口 + */ + @Bean + public CasAuthenticationEntryPoint casAuthenticationEntryPoint() { + CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint(); + casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl()); + casAuthenticationEntryPoint.setServiceProperties(serviceProperties()); + return casAuthenticationEntryPoint; + } + + /** + * 指定service相关信息 + */ + @Bean + public ServiceProperties serviceProperties() { + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl()); + serviceProperties.setAuthenticateAllArtifacts(true); + return serviceProperties; + } + + /** + * CAS认证过滤器 + */ + @Bean + public CasAuthenticationFilter casAuthenticationFilter() throws Exception { + CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter(); + casAuthenticationFilter.setAuthenticationManager(authenticationManager()); + casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl()); + casAuthenticationFilter.setAuthenticationSuccessHandler(casAuthenticationSuccessHandler); + return casAuthenticationFilter; + } + + /** + * cas 认证 Provider + */ + @Bean + public CasAuthenticationProvider casAuthenticationProvider() { + CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider(); + casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService); + casAuthenticationProvider.setServiceProperties(serviceProperties()); + casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator()); + casAuthenticationProvider.setKey("casAuthenticationProviderKey"); + return casAuthenticationProvider; + } + + @Bean + public Cas20ServiceTicketValidator cas20ServiceTicketValidator() { + return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl()); + } + + /** + * 单点登出过滤器 + */ + @Bean + public SingleSignOutFilter singleSignOutFilter() { + SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter(); + singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl()); + singleSignOutFilter.setIgnoreInitConfiguration(true); + return singleSignOutFilter; + } + + /** + * 请求单点退出过滤器 + */ + @Bean + public LogoutFilter casLogoutFilter() { + LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(), + new SecurityContextLogoutHandler()); + logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl()); + return logoutFilter; + } +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/TokenService.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/TokenService.java new file mode 100644 index 00000000..2e1e2782 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/TokenService.java @@ -0,0 +1,4 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +public class TokenService { +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/UserDetailsServiceImpl.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/UserDetailsServiceImpl.java new file mode 100644 index 00000000..730417cd --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/UserDetailsServiceImpl.java @@ -0,0 +1,30 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Primary; +import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken; +import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.HashSet; +import java.util.Set; + + +public class UserDetailsServiceImpl implements AuthenticationUserDetailsService { + @Override + public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException { + System.out.println("getCredentials:" + token.getCredentials()); + String username = token.getName(); + System.out.println(username); + UserInfo userInfo = new UserInfo(); + userInfo.setUsername("admin"); + userInfo.setName("admin"); + Set authorities = new HashSet(); + AuthorityInfo authorityInfo = new AuthorityInfo("TEST"); + authorities.add(authorityInfo); + userInfo.setAuthorities(authorities); + return userInfo; + } +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/UserInfo.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/UserInfo.java new file mode 100644 index 00000000..5618f1ae --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/config/UserInfo.java @@ -0,0 +1,98 @@ +package com.unicom.sso.bigdata.cas.demo.config; + +import lombok.Getter; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.HashSet; +import java.util.Set; + +public class UserInfo implements UserDetails { + private static final long serialVersionUID = -1041327031937199938L; + + /** + * 用户ID + */ + @Getter + private Long id; + + /** + * 用户名称 + */ + @Getter + private String name; + + /** + * 登录名称 + */ + @Getter + private String username; + + /** + * 登录密码 + */ + @Getter + private String password; + + private boolean isAccountNonExpired = true; + + private boolean isAccountNonLocked = true; + + private boolean isCredentialsNonExpired = true; + + private boolean isEnabled = true; + + @Getter + private Set authorities = new HashSet(); + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isAccountNonExpired() { + return isAccountNonExpired; + } + + public void setAccountNonExpired(boolean accountNonExpired) { + isAccountNonExpired = accountNonExpired; + } + + public boolean isAccountNonLocked() { + return isAccountNonLocked; + } + + public void setAccountNonLocked(boolean accountNonLocked) { + isAccountNonLocked = accountNonLocked; + } + + public boolean isCredentialsNonExpired() { + return isCredentialsNonExpired; + } + + public void setCredentialsNonExpired(boolean credentialsNonExpired) { + isCredentialsNonExpired = credentialsNonExpired; + } + + public boolean isEnabled() { + return isEnabled; + } + + public void setEnabled(boolean enabled) { + isEnabled = enabled; + } + + public void setAuthorities(Set authorities) { + this.authorities = authorities; + } +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/controller/HelloController.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/controller/HelloController.java new file mode 100644 index 00000000..8ab815f8 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/controller/HelloController.java @@ -0,0 +1,63 @@ +package com.unicom.sso.bigdata.cas.demo.controller; + +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.Assertion; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * cas 回调地址 + * @author baihz10 + * @date 2023/7/7 15:44 + */ +@Controller +public class HelloController { + + @GetMapping("/hello") + public String home(Model model, HttpServletRequest request) { +// String token =request.getParameter("token"); +// System.out.println("token : "+token); +// Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); +// +// String username= assertion.getPrincipal().getName(); +// System.out.println("cas user:"+username); +// +// username = Optional.ofNullable(username).orElse("anonymous"); +// Map attributes = Optional.ofNullable(assertion.getPrincipal().getAttributes()).orElse(new HashMap<>()); +// +// model.addAttribute("username", username); + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + System.out.println("当前用户信息:" + auth.getPrincipal()); + return "home"; + } + + @GetMapping("/hello2") + public String home2(Model model, HttpServletRequest request) { + String token =request.getParameter("token"); + System.out.println("token : "+token); + Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); + + String username= assertion.getPrincipal().getName(); + System.out.println("cas user:"+username); + + username = Optional.ofNullable(username).orElse("anonymous"); + Map attributes = Optional.ofNullable(assertion.getPrincipal().getAttributes()).orElse(new HashMap<>()); + + model.addAttribute("username", username); + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + System.out.println("当前用户信息:" + auth.getPrincipal()); + return "home"; + } + + +} diff --git a/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/controller/SsoLoginController.java b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/controller/SsoLoginController.java new file mode 100644 index 00000000..96f77456 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/java/com/unicom/sso/bigdata/cas/demo/controller/SsoLoginController.java @@ -0,0 +1,29 @@ +package com.unicom.sso.bigdata.cas.demo.controller; + +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.Assertion; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +/** + * 控制类 + * @author baihz10 + * @date 2023/7/7 15:44 + */ +@Controller +public class SsoLoginController { + + @GetMapping("/caslogin") + public String home(Model model,HttpServletRequest request) { + Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); + + String username= assertion.getPrincipal().getName(); + username = Optional.ofNullable(username).orElse("anonymous"); + model.addAttribute("username", username); + return "cas/login"; + } +} diff --git a/integrations/spring-security-cas-demo/src/main/resources/application.properties b/integrations/spring-security-cas-demo/src/main/resources/application.properties new file mode 100644 index 00000000..8ce98040 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/resources/application.properties @@ -0,0 +1,25 @@ +server.port=10002 + +#spring.profiles.active=cas-test + +base.url=http://cas.demo.maxkey.top:10002 +cas.server.url=http://192.168.202.102:8080/sign/authz/cas + +#CAS +cas.server.host.url=http://192.168.202.102:8080/sign/authz/cas +#CAS???? + +#CAS?????? +cas.server.host.login_url=${cas.server.host.url}/login +#CAS?????? +cas.server.host.logout_url=${cas.server.host.url}/logout?service=${app.server.host.url} +# ?????? +app.casEnable=true +app.server.host.url=http://192.168.202.102:${server.port} + +#?????? +app.server.host.login_url=/ +#?????? +app.server.host.logout_url=/logout +#?????? +app.server.host.web_url=http://192.168.202.102/index \ No newline at end of file diff --git a/integrations/spring-security-cas-demo/src/main/resources/templates/cas/login.html b/integrations/spring-security-cas-demo/src/main/resources/templates/cas/login.html new file mode 100644 index 00000000..9037cfa7 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/resources/templates/cas/login.html @@ -0,0 +1,13 @@ + + + + + + sso cas demo + + + +

Login with CAS

+login user : [[${username}]] + + \ No newline at end of file diff --git a/integrations/spring-security-cas-demo/src/main/resources/templates/home.html b/integrations/spring-security-cas-demo/src/main/resources/templates/home.html new file mode 100644 index 00000000..97284838 --- /dev/null +++ b/integrations/spring-security-cas-demo/src/main/resources/templates/home.html @@ -0,0 +1,13 @@ + + + + + home + + +hello cas +
+hello [[${username}]] +userInfo: [[${userInfo}]] + + \ No newline at end of file