diff --git a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/config/authorization2/AuthorizationResourceConfig.java b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/config/authorization2/AuthorizationResourceConfig.java index a1657b2..c05f50e 100644 --- a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/config/authorization2/AuthorizationResourceConfig.java +++ b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/config/authorization2/AuthorizationResourceConfig.java @@ -2,6 +2,7 @@ package com.micro.fast.security.app.config.authorization2; import com.micro.fast.security.app.config.component.MsAuthenticationFailureHandler; import com.micro.fast.security.app.config.component.MsAuthenticationSuccessHandler; +import com.micro.fast.security.app.social.OpenIdAuthenticationSecurityConfig; import com.micro.fast.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig; import com.micro.fast.security.core.master.SecurityConstants; import com.micro.fast.security.core.master.SecurityProperties; @@ -45,6 +46,9 @@ public class AuthorizationResourceConfig extends ResourceServerConfigurerAdapter @Autowired private ValidateCodeRepository validateCodeRepository; + @Autowired + private OpenIdAuthenticationSecurityConfig openIdAuthenticationSecurityConfig; + @Override public void configure(HttpSecurity http) throws Exception { //创建自定义的验证码过滤器,填入身份认证失败处理对象 @@ -55,7 +59,7 @@ public class AuthorizationResourceConfig extends ResourceServerConfigurerAdapter validateCodeFilter.afterPropertiesSet(); http - //添加自定义的验证码处理器,在用户密码校验过滤器之前 + //添加自定义的验证码处理器,在用户密码校验过滤器的方式获取令牌之前 .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) //使用表单登录 .formLogin() @@ -93,6 +97,9 @@ public class AuthorizationResourceConfig extends ResourceServerConfigurerAdapter .and() //应用springSocial的配置,在接收到特殊的请求的时候进行社交登录.例如 /auth/qq 其中/auth是默认的前缀,qq是providerId .apply(springSocialConfigurer) + .and() + //openid + .apply(openIdAuthenticationSecurityConfig); ; } } diff --git a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationFilter.java b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationFilter.java new file mode 100644 index 0000000..35d6cd6 --- /dev/null +++ b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationFilter.java @@ -0,0 +1,98 @@ +package com.micro.fast.security.app.social; + +import com.micro.fast.security.core.master.SecurityConstants; +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 org.springframework.util.Assert; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * openId登录身份认证过滤器 + * @author lsy + */ +public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + + /** + * 从请求中获取openId的key + */ + private String openIdParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_OPNEID; + + /** + * 从请求中获取providerId的key + */ + private String providerIdParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_PROVIDERID; + /** + * 是不是只处理post请求 + */ + private boolean postOnly = true; + + /** + * 对/authentication/openid路径的post请求进行身份登录操作 + */ + public OpenIdAuthenticationFilter() { + super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESS_URL_OPENID, "POST")); + } + + /** + * 主要的身份认证方法 + * @param request + * @param response + * @return + * @throws AuthenticationException + */ + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + if (this.postOnly && !request.getMethod().equals("POST")) { + throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); + } else { + //获取请求中的mobile + String openid = this.obtainMobile(request); + if (openid == null) { + openid = ""; + } + openid = openid.trim(); + String providerId = request.getParameter(this.providerIdParameter); + if (providerId == null){ + providerId = ""; + } + providerId = providerId.trim(); + OpenIdAuthenticationToken authRequest = new OpenIdAuthenticationToken(openid,providerId); + this.setDetails(request, authRequest); + //调用AuthenticationManager中的方法 + return this.getAuthenticationManager().authenticate(authRequest); + } + } + + /** + * 获取openId的方法 + * @param request + * @return + */ + protected String obtainMobile(HttpServletRequest request) { + return request.getParameter(this.openIdParameter); + } + + protected void setDetails(HttpServletRequest request, OpenIdAuthenticationToken authRequest) { + authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + } + + public void setOpenIdParameter(String openIdParameter) { + Assert.hasText(openIdParameter, "Username parameter must not be empty or null"); + this.openIdParameter = openIdParameter; + } + + public void setPostOnly(boolean postOnly) { + this.postOnly = postOnly; + } + + public final String getOpenIdParameter() { + return this.openIdParameter; + } + +} diff --git a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationProvider.java b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationProvider.java new file mode 100644 index 0000000..89e7fc4 --- /dev/null +++ b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationProvider.java @@ -0,0 +1,78 @@ +package com.micro.fast.security.app.social; + +import com.micro.fast.security.core.authentication.mobile.SmsCodeAuthenticationToken; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.social.connect.UsersConnectionRepository; +import org.springframework.social.security.SocialUserDetails; +import org.springframework.social.security.SocialUserDetailsService; + +import java.util.HashSet; +import java.util.Set; + +/** + * 进行openId的身份认证提供类 + * @author lsy + */ +public class OpenIdAuthenticationProvider implements AuthenticationProvider { + + private SocialUserDetailsService userDetailsService; + + private UsersConnectionRepository usersConnectionRepository; + /** + * 返回经过验证的Authentication + * @param authentication + * @return + * @throws AuthenticationException + */ + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + OpenIdAuthenticationToken openIdAuthenticationToken = (OpenIdAuthenticationToken) authentication; + Set providerUserIds = new HashSet<>(); + String principal = (String)openIdAuthenticationToken.getPrincipal(); + providerUserIds.add(principal); + Set userIds = usersConnectionRepository.findUserIdsConnectedTo(openIdAuthenticationToken.getProviderId(), providerUserIds); + if (CollectionUtils.isEmpty(userIds) || userIds.size()!=1) { + throw new InternalAuthenticationServiceException("无法获取用户的信息"); + } + String userId = userIds.iterator().next(); + UserDetails socialUserDetails = userDetailsService.loadUserByUserId(userId); + if (socialUserDetails == null){ + throw new InternalAuthenticationServiceException("无法获取用户的信息"); + } + OpenIdAuthenticationToken openIdAuthenticationTokenResult = new OpenIdAuthenticationToken(socialUserDetails, socialUserDetails.getAuthorities()); + openIdAuthenticationTokenResult.setDetails(openIdAuthenticationToken.getDetails()); + return openIdAuthenticationTokenResult; + } + + /** + * 这个类是不是支持传入的token的校验 + * @param authenticate + * @return + */ + @Override + public boolean supports(Class authenticate) { + return OpenIdAuthenticationToken.class.isAssignableFrom(authenticate); + } + + public SocialUserDetailsService getUserDetailsService() { + return userDetailsService; + } + + public void setUserDetailsService(SocialUserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public UsersConnectionRepository getUsersConnectionRepository() { + return usersConnectionRepository; + } + + public void setUsersConnectionRepository(UsersConnectionRepository usersConnectionRepository) { + this.usersConnectionRepository = usersConnectionRepository; + } +} diff --git a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationSecurityConfig.java b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationSecurityConfig.java new file mode 100644 index 0000000..2b0ed5c --- /dev/null +++ b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationSecurityConfig.java @@ -0,0 +1,57 @@ +package com.micro.fast.security.app.social; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.social.connect.UsersConnectionRepository; +import org.springframework.social.security.SocialUserDetailsService; +import org.springframework.stereotype.Component; + +/** + * @author lsy + * openId登录的配置类 + */ +@Component +public class OpenIdAuthenticationSecurityConfig extends SecurityConfigurerAdapter{ + + @Autowired + @Qualifier("msAuthenticationSuccessHandler") + private AuthenticationSuccessHandler authenticationSuccessHandler; + + @Autowired + @Qualifier("msAuthenticationFailureHandler") + private AuthenticationFailureHandler authenticationFailureHandler; + + @Autowired + @Qualifier("msUserDetailsServiceImpl") + private SocialUserDetailsService userDetailsService ; + + @Autowired + private UsersConnectionRepository usersConnectionRepository; + + @Override + public void configure(HttpSecurity http) throws Exception { + + OpenIdAuthenticationFilter OpenIdAuthenticationFilter = new OpenIdAuthenticationFilter(); + //将短信验证码登录过滤器放入AuthenticationManager,方便后续调用 + OpenIdAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); + OpenIdAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); + OpenIdAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler); + + //将根据手机号获取用户信息的对象放入短信验证码的provider中 + OpenIdAuthenticationProvider openIdAuthenticationProvider = new OpenIdAuthenticationProvider(); + openIdAuthenticationProvider.setUserDetailsService(userDetailsService); + openIdAuthenticationProvider.setUsersConnectionRepository(usersConnectionRepository); + //将自定义的provider放入authenticaionManger管理的集合中去 + http.authenticationProvider(openIdAuthenticationProvider) + //将自定义的过滤器添加到用户账号密码校验过滤器后面 + .addFilterAfter(OpenIdAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationToken.java b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationToken.java new file mode 100644 index 0000000..cd8d028 --- /dev/null +++ b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/OpenIdAuthenticationToken.java @@ -0,0 +1,74 @@ +package com.micro.fast.security.app.social; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * APP社交登陆中使用openId进行登录的token + * @author lsy + */ +public class OpenIdAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = 1L; + + private final Object principal; + + /** + * 服务提供商的id + */ + private String providerId; + + /** + * 在没有进行身份认证的时候存储手机号 + * @param openId openId + */ + public OpenIdAuthenticationToken(String openId,String providerId) { + super((Collection)null); + this.principal = openId; + this.providerId = providerId; + this.setAuthenticated(false); + } + + /** + * @param principal + * @param authorities + */ + public OpenIdAuthenticationToken(Object principal, Collection authorities) { + super(authorities); + this.principal = principal; + super.setAuthenticated(true); + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getPrincipal() { + return this.principal; + } + + @Override + 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"); + } else { + super.setAuthenticated(false); + } + } + + @Override + public void eraseCredentials() { + super.eraseCredentials(); + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } +} diff --git a/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/package-info.java b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/package-info.java new file mode 100644 index 0000000..3757f96 --- /dev/null +++ b/boot-starter/boot-starter-security/boot-starter-security-app/src/main/java/com/micro/fast/security/app/social/package-info.java @@ -0,0 +1 @@ +package com.micro.fast.security.app.social; \ No newline at end of file diff --git a/boot-starter/boot-starter-security/boot-starter-security-core/src/main/java/com/micro/fast/security/core/master/SecurityConstants.java b/boot-starter/boot-starter-security/boot-starter-security-core/src/main/java/com/micro/fast/security/core/master/SecurityConstants.java index 335d56c..5ca4331 100644 --- a/boot-starter/boot-starter-security/boot-starter-security-core/src/main/java/com/micro/fast/security/core/master/SecurityConstants.java +++ b/boot-starter/boot-starter-security/boot-starter-security-core/src/main/java/com/micro/fast/security/core/master/SecurityConstants.java @@ -41,4 +41,18 @@ public interface SecurityConstants { */ String DEFAULT_SESSION_INVALID_URL = "/ms-session-invalid.html"; + /** + * 获取openId的key + */ + String DEFAULT_PARAMETER_NAME_OPNEID = "openid"; + + /** + * 获取providerId的key + */ + String DEFAULT_PARAMETER_NAME_PROVIDERID = "providerId"; + + /** + * openID获取access_token的步骤 + */ + String DEFAULT_LOGIN_PROCESS_URL_OPENID = "/authentication/openId"; }