Springboot + Security + Cas 整合

版权声明:如需转载,请备注链接: https://blog.csdn.net/W_Meng_H/article/details/86673630

参考:http://blog.csdn.net/cl_andywin/article/details/53998986

我的整合是基于Springboot 2.0版本  +  Cas 5.X版本

获取的用户信息,需要自己在Cas的服务端进行设置用户信息的返回内容

CAS默认只回显用户名

一、pom.xml文件,加入以下内容

<!--cas的客户端 -->
<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>3.5.0</version>
</dependency>
<!-- security 对CAS支持 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
</dependency>
<!-- security taglibs -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
</dependency>

二、application.properties文件,加入以下内容

#CAS服务地址 
security.cas.server.host.url=http://XXXXX/cas 
#CAS服务登录地址 
security.cas.server.host.login_url=${security.cas.server.host.url}/login 
#CAS服务登出地址 
security.cas.server.host.logout_url=${security.cas.server.host.url}/logout?service=${security.app.server.host.url}
#应用访问地址 
security.app.server.host.url=http://localhost:8080
#应用登录地址
security.app.login.url=/user/login
#应用登出地址 
security.app.logout.url=/logout 

 

三、security配置文件SecurityConfig,项目启动的时候执行,初始化security和cas的设置

package org.big.encyclopedia.config;
 
import org.big.encyclopedia.service.CustomUserDetailsService;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.Cas30ServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
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.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
 
 
/**
 * Security的配置
 */ 
@Configuration
@EnableWebSecurity //启用web权限
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法验证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private CasProperties casProperties;
	
	/**定义认证用户信息获取来源,密码校验规则等*/
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		super.configure(auth);
		auth.authenticationProvider(casAuthenticationProvider());
	}
	
	/**定义安全策略*/
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()//配置安全策略
			.antMatchers("/","/home/**","/seach/**","/user/**").permitAll()//定义/请求不需要验证
			.antMatchers("/login/**").authenticated()//login下请求需要验证
			.and()
		.logout()
			.permitAll()//定义logout不需要验证
			.and()
		.formLogin();//使用form表单登录
		
		http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())
			.and()
			.addFilter(casAuthenticationFilter())
			.addFilterBefore(casLogoutFilter(), LogoutFilter.class)
			.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);
		
		//http.csrf().disable(); //禁用CSRF
		
	}
	
	/**指定service相关信息*/
	@Bean
	public ServiceProperties serviceProperties() {
		ServiceProperties serviceProperties = new ServiceProperties();
		serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());
		serviceProperties.setAuthenticateAllArtifacts(true);
		return serviceProperties;
	}
	
	/**认证的入口*/
	@Bean
	public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
		CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
		casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());
		casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
		return casAuthenticationEntryPoint;
	}
	
	/**CAS认证过滤器*/
	@Bean
	public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
		CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
		casAuthenticationFilter.setAuthenticationManager(authenticationManager());
		casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl());
		return casAuthenticationFilter;
	}
	
	@Bean
	public Cas30ServiceTicketValidator cas30ServiceTicketValidator() {
		return new Cas30ServiceTicketValidator(casProperties.getCasServerUrl());
	}
	
	/**cas 认证 Provider*/
	@Bean
	public CasAuthenticationProvider casAuthenticationProvider() {
		CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
		casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());
		//casAuthenticationProvider.setUserDetailsService(customUserDetailsService()); //这里只是接口类型,实现的接口不一样,都可以的。
		casAuthenticationProvider.setServiceProperties(serviceProperties());
		casAuthenticationProvider.setTicketValidator(cas30ServiceTicketValidator());
		casAuthenticationProvider.setKey("casAuthenticationProviderKey");
		return casAuthenticationProvider;
	}
	
	/**用户自定义的AuthenticationUserDetailsService*/
	@Bean
	public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService(){
		return new CustomUserDetailsService();
	}
	
	/**单点登出过滤器*/
	@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;
	}
}

这里service里设置的路径是 http://localhost:8080/user/login ,保证这个路径能正常返回,登陆成功后才能正常返回ticket,执行后面的CustomUserDetailsService类中的loadUserDetails方法。

四、CasProperties类,用于将properties文件指定的内容注入以方便使用

package org.big.encyclopedia.config;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
/**
 * CAS的配置参数
 */
@Component
public class CasProperties {
	
        @Value("${security.cas.server.host.url}")
        private String casServerUrl;
 
        @Value("${security.cas.server.host.login_url}")
        private String casServerLoginUrl;
 
        @Value("${security.cas.server.host.logout_url}")
        private String casServerLogoutUrl;
 
        @Value("${security.app.server.host.url}")
        private String appServerUrl;
 
        @Value("${security.app.login.url}")
        private String appLoginUrl;
 
        @Value("${security.app.logout.url}")
        private String appLogoutUrl;

	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 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;
	}

}

五、定义CustomUserDetailsService类

package org.big.encyclopedia.service;
 
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.big.encyclopedia.entity.AuthorityInfo;
import org.big.encyclopedia.entity.UserInfo;
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 org.springframework.stereotype.Service;
 
/**
 * 用于加载用户信息 实现UserDetailsService接口,或者实现AuthenticationUserDetailsService接口
 */
@Service
public class CustomUserDetailsService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {           
    @Override
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
                // 结合具体的逻辑去实现用户认证,并返回继承UserDetails的用户对象;
		System.out.println("当前的用户名是:"+token.getName());
		
		//获取用户信息
		UserInfo userInfo = new UserInfo();
		userInfo.setUsername(token.getName());
		userInfo.setRole("ROLE_USER");
		Map<String, Object> userAttributess = token.getAssertion().getPrincipal().getAttributes();
		//System.out.println(userAttributess.toString());
		if (userAttributess != null) {
			userInfo.setId( String.valueOf(userAttributess.get("id")));
			userInfo.setNickname( String.valueOf(userAttributess.get("nickname")));
			userInfo.setRealName( String.valueOf(userAttributess.get("real_name")));
			userInfo.setEmail( String.valueOf(userAttributess.get("email")));
			userInfo.setCountryCode( String.valueOf(userAttributess.get("country_code")));
		}
		
		System.out.println(userInfo.toString());
                return userInfo;
    }

}

 

六、定义UserInfo类,用于加载当前用户信息,实现UserDetails接口

package org.big.encyclopedia.entity;
 
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
 
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
 
/**
 * 用户信息
 */
public class UserInfo extends User implements UserDetails {
	private static final long serialVersionUID = -1041327031937199938L;
 
        private String id;
        private String username;
        private String email;
        private String countryCode;
        private String mobile;
        private String nickname;
        private String role;
        private String realName;
    
        private String password;
    
	private boolean isAccountNonExpired = true; //是否过期
 
	private boolean isAccountNonLocked = true;//账户未锁定为true
 
	private boolean isCredentialsNonExpired = true;//证书不过期为true
 
	private boolean isEnabled = true;//是否可用

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            if(this.getRole() == null || this.getRole().length() <1){
                return AuthorityUtils.commaSeparatedStringToAuthorityList("");
            }
            else{
                return AuthorityUtils.commaSeparatedStringToAuthorityList(this.getRole());
            }
        }


	public String getId() {
		return id;
	}


	public void setId(String id) {
		this.id = id;
	}


	public String getUsername() {
		return username;
	}


	public void setUsername(String username) {
		this.username = username;
	}


	public String getEmail() {
		return email;
	}


	public void setEmail(String email) {
		this.email = email;
	}


	public String getCountryCode() {
		return countryCode;
	}


	public void setCountryCode(String countryCode) {
		this.countryCode = countryCode;
	}


	public String getMobile() {
		return mobile;
	}


	public void setMobile(String mobile) {
		this.mobile = mobile;
	}


	public String getNickname() {
		return nickname;
	}


	public void setNickname(String nickname) {
		this.nickname = nickname;
	}


	public String getRole() {
		return role;
	}


	public void setRole(String role) {
		this.role = role;
	}


	public String getRealName() {
		return realName;
	}


	public void setRealName(String realName) {
		this.realName = realName;
	}


	public String getPassword() {
		return password;
	}


	public void setPassword(String password) {
		this.password = password;
	}


	public boolean isAccountNonExpired() {
		return isAccountNonExpired;
	}


	public void setAccountNonExpired(boolean isAccountNonExpired) {
		this.isAccountNonExpired = isAccountNonExpired;
	}


	public boolean isAccountNonLocked() {
		return isAccountNonLocked;
	}


	public void setAccountNonLocked(boolean isAccountNonLocked) {
		this.isAccountNonLocked = isAccountNonLocked;
	}


	public boolean isCredentialsNonExpired() {
		return isCredentialsNonExpired;
	}


	public void setCredentialsNonExpired(boolean isCredentialsNonExpired) {
		this.isCredentialsNonExpired = isCredentialsNonExpired;
	}


	public boolean isEnabled() {
		return isEnabled;
	}


	public void setEnabled(boolean isEnabled) {
		this.isEnabled = isEnabled;
	}


	@Override
	public String toString() {
		return "UserInfo [id=" + id + ", username=" + username + ", email=" + email + ", countryCode=" + countryCode
				+ ", mobile=" + mobile + ", nickname=" + nickname + ", role=" + role + ", realName=" + realName
				+ ", password=" + password + ", isAccountNonExpired=" + isAccountNonExpired + ", isAccountNonLocked="
				+ isAccountNonLocked + ", isCredentialsNonExpired=" + isCredentialsNonExpired + ", isEnabled="
				+ isEnabled + "]";
	}

}

七、启动服务端,登陆之后,回显数据

UserInfo [
	id=XXXX, 
	username=XXXX, 
	[email protected], 
	countryCode=null, 
	mobile=null, 
	nickname=null, 
	role=ROLE_USER, 
	realName=null, 
	password=null, 
	isAccountNonExpired=true, 
	isAccountNonLocked=true, 
	isCredentialsNonExpired=true, 
	isEnabled=true
]

 

八、常见错误(无限重定向 和 401错误)

https://blog.csdn.net/mushuntaosama/article/details/72763864

猜你喜欢

转载自blog.csdn.net/W_Meng_H/article/details/86673630