步骤:
1.继承 WebSecurityConfigurerAdapter.class,其中使用两个过滤器,一个spring scurity自带的UsernamePasswordAuthenticationFilter,一个是自定义的过滤器ZTSSOAuthenticationProcessingFilter ,他们都继承AbstractAuthenticationProcessingFilter,该filter的功能是去指定拦截界面发送的post请求,然后加入到filter chain 中去。
import com.idoipo.ibt.service.LoginAuthenticationSuccessHandler; import com.idoipo.ibt.service.SmsAuthenticationProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; 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.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; /** * Created by pingli on 2018-10-13. */ @Configuration @EnableWebSecurity @SuppressWarnings("unused") public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter{ @Value("${com.cookie}") private String sessionCookieName; @Value("${com..cookie.token}") private String tokenCookie; @Value("${com.cookie.timer}") private String timerCookie; @Value("${com.cookie.curp}") private String curpCookie; @Value("${com.cookie.gw}") private String gwCookie; @Value("${com.cookie.commondomain}") private String cdCookie; @Value("${com.index}") private String homeUrl; @Value("${com.main}") private String mainUrl; @Value("${zt.ssoUrl}") private String ssoUrl; @Value("${zt.appId}") private String appId; @Value("${com.idoipo.infras.ibt}") private String ibtUrl; @Autowired private SmsAuthenticationProvider authenticationProvider; @Autowired private ZTSSOAuthenticationProvider ztssoAuthenticationProvider; @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Autowired private LoginAuthenticationSuccessHandler successHandler; @Override protected void configure(HttpSecurity http) throws Exception { String logoutUrl="/logout"; String loginUrl="/login"; String loginMiddleUrl="/SsoLogin"; String ssoUrlRequest = ssoUrl+"?a="+appId+"&"+"r="+ibtUrl+loginMiddleUrl; http.formLogin()//处理登录 .loginPage(ssoUrlRequest) .successHandler(successHandler) .loginProcessingUrl(loginUrl) .usernameParameter("mobile") .passwordParameter("check") .failureUrl(ssoUrlRequest) .permitAll() .and() .authorizeRequests()//请求授权 .antMatchers("/",homeUrl,"/file/supload","/dist/**",loginMiddleUrl).permitAll()//首页与前端资源可以直接访问 .antMatchers(logoutUrl,"/heartbeat").authenticated() .antMatchers(loginUrl).anonymous() .anyRequest().authenticated() .and() .logout() .logoutUrl(logoutUrl) .logoutSuccessUrl(homeUrl) .invalidateHttpSession(true) .deleteCookies(sessionCookieName,tokenCookie,timerCookie,curpCookie,gwCookie,cdCookie) .logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, "GET")) .and() .csrf() .disable() .addFilterAt(ztssoAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);//增加过滤/SsoLogin接口请求 } @Bean public ZTSSOAuthenticationProcessingFilter ztssoAuthenticationFilter() { ZTSSOAuthenticationProcessingFilter filter = new ZTSSOAuthenticationProcessingFilter(); filter.setAuthenticationManager(authenticationManager); filter.setAuthenticationSuccessHandler(successHandler); return filter; } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(this.authenticationProvider) .authenticationProvider(this.ztssoAuthenticationProvider); } }
import com.idoipo.ibt.controllers.HomeController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by pingli on 2018-10-06 * 拦截请求 */ public class ZTSSOAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { private Logger logger = LoggerFactory.getLogger(ZTSSOAuthenticationProcessingFilter.class); public static final String SPRING_SECURITY_FORM_ZT_TOKEN = "Token"; private String ZTToken = SPRING_SECURITY_FORM_ZT_TOKEN; private boolean postOnly = true; public ZTSSOAuthenticationProcessingFilter() { super(new AntPathRequestMatcher("/SsoLogin", "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { logger.info("进入sso过滤器"); if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String token = obtainToken(request); if (token == null) { token = ""; } AbstractAuthenticationToken authRequest = new ZTSSOAuthenticationToken(null,token); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } protected String obtainToken(HttpServletRequest request) { return request.getParameter(ZTToken); } protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } }
2
ZTSSOAuthenticationProcessingFilter 拦截到SsoLogin 的post请求后,需要一个继承
AbstractAuthenticationToken的token类来存得到的第三方的token参数。
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityCoreVersion; import java.util.Collection; /** * Created by pingli on 2018-10-06 * 生成登录session,同用户不用再校验 */ public class ZTSSOAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final Object principal; private String credentials; public ZTSSOAuthenticationToken(Object principal,String credentials) { super(null); this.principal = principal; this.credentials = credentials; super.setAuthenticated(true); } public ZTSSOAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; this.credentials = null; super.setAuthenticated(true); // must use super, as we override } // ~ Methods // ======================================================================================================== public String getCredentials() { return this.credentials; } public Object getPrincipal() { return this.principal; } public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { if (isAuthenticated) { throw new IllegalArgumentException( "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); } super.setAuthenticated(false); } @Override public void eraseCredentials() { super.eraseCredentials(); credentials = null; } }
3.得到参数后,需要根据token,去第三方查询用户信息,用户生成自己系统的token,这一步在实现了
AuthenticationProvider的类中去处理,这个类需要去指定
ZTSSOAuthenticationToken 支持他,所以需要重写
supports方法,从而关联上ZTSSOAuthenticationProcessingFilter ,
ZTSSOAuthenticationToken ,
AuthenticationProvider这三个类,filter得到请求,token去保存参数,provider去获取支持的token类的参数,从而完成一条线,后续其他验证也可以采用该种方式去增加,注意如果要增加成功后的处理,请注意
继承了WebSecurityConfigurerAdapter中添加
filter.setAuthenticationSuccessHandler(successHandler);去设置授权成功后可以进行一些后续处理,比如生成token字符串到前台,或者指定默认的跳转路径
import com.idoipo.ibt.bto.AccountInfo; import com.idoipo.ibt.bto.UserDetail; import com.idoipo.ibt.service.UserService; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; /** * Created by pingli on 2018-10-06 * sso验证类 */ @Component public class ZTSSOAuthenticationProvider implements AuthenticationProvider { private Logger logger= LoggerFactory.getLogger(ZTSSOAuthenticationProvider.class); @Autowired private UserService userService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String ZTToken = (authentication.getCredentials()==null)?null:authentication.getCredentials().toString(); if (StringUtils.isEmpty(ZTToken)) { throw new BadCredentialsException("token不能为空"); } AccountInfo accountInfo = userService.getUserInfoBySSO(ZTToken); logger.info("当前sso反回账号信息={}",accountInfo); UserDetail userDetail = userService.getTokenByPsnId(accountInfo.getAccount()); ZTSSOAuthenticationToken result = new ZTSSOAuthenticationToken(userDetail,userDetail.getJwtToken()); result.setDetails(authentication.getDetails()); return result; } @Override public boolean supports(Class<?> authentication) { logger.info(this.getClass().getName() + "---supports"); return (ZTSSOAuthenticationToken.class.isAssignableFrom(authentication)); } //后续权限控制 // private Set<GrantedAuthority> listUserGrantedAuthorities(Long uid) { // Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); // if (null == uid) { // return authorities; // } // authorities.add(new SimpleGrantedAuthority("ROLE_USER")); // return authorities; // } }
import com.idoipo.ibt.bto.UserDetail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.List; /** * Created by Jemmy on 2017-09-08. */ @Component @SuppressWarnings("unused") public class LoginAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler{ private Logger logger= LoggerFactory.getLogger(LoginAuthenticationSuccessHandler.class); @Value("${com.pages.main}") private String mainUrl; @Value("${com.common.domain}") private String commonDomain; public LoginAuthenticationSuccessHandler() { super(); } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { logger.info("登录用户服务成功"); this.setDefaultTargetUrl(mainUrl); this.setAlwaysUseDefaultTargetUrl(true); UserDetail userDetail=(UserDetail)authentication.getPrincipal(); // request.setAttribute("TOKEN",userDetail.getJwtToken()); Cookie cookie=new Cookie("IBT-TOKEN",userDetail.getJwtToken()); cookie.setPath("/"); cookie.setHttpOnly(false); cookie.setDomain(commonDomain); response.addCookie(cookie); super.onAuthenticationSuccess(request, response, authentication); //request.getRequestDispatcher("redirect:/main").forward(request,response); } }