文章目录
介绍
上一章讲到了自定义登录返回token的方式登录。但如果用户要社交登录,同样也得返回token给用户。社交登录有两种模式,一种是app传providerId和openId,一种是app传了code过来。这里先讲第一种,
实现功能
社交登录,app传来providerId和openId登录返回token。
步骤
实现前提:该providerId和openId必须在数据库中存在。
实现的过程其实和手机号验证码登录一样。
编写OpenIdAuthenticationToken
/**
* @author lvhaibao
* @description
* @date 2019/1/8 0008 14:40
*/
public class OpenIdAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 420L;
/**
* openid
*/
private final Object principal;
/**
* 服务提供上ID
*/
private String providerId;
OpenIdAuthenticationToken(String openId, String providerId) {
super(null);
this.principal =openId;
this.providerId = providerId;
setAuthenticated(false);
}
OpenIdAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> 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 eraseCredentials() {
super.eraseCredentials();
}
@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);
}
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
}
编写OpenIdAuthenticationProvider
/**
* @author lvhaibao
* @description
* @date 2019/1/8 0008 14:46
*/
public class OpenIdAuthenticationProvider implements AuthenticationProvider {
private SocialUserDetailsService myUserDetailsService;
private UsersConnectionRepository usersConnectionRepository;
@Override
public boolean supports(Class<?> authentication) {
//支持OpenIdAuthenticationToken来验证
return OpenIdAuthenticationToken.class.isAssignableFrom(authentication);
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//这个authentication就是OpenIdAuthenticationToken
OpenIdAuthenticationToken authenticationToken = (OpenIdAuthenticationToken) authentication;
//校验手机号
Set<String> providerUserIds = new HashSet<>();
providerUserIds.add((String) authenticationToken.getPrincipal());
Set<String> userIds = usersConnectionRepository.findUserIdsConnectedTo(authenticationToken.getProviderId(),providerUserIds);
if(CollectionUtils.isEmpty(userIds) || userIds.size() != 1){
throw new InternalAuthenticationServiceException("无法获取用户信息");
}
String userId = userIds.iterator().next();
UserDetails user = myUserDetailsService.loadUserByUserId(userId);
if(user == null ){
throw new InternalAuthenticationServiceException("无法获取用户信息");
}
//这时候已经认证成功了
OpenIdAuthenticationToken authenticationResult = new OpenIdAuthenticationToken(user, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
public SocialUserDetailsService getMyUserDetailsService() {
return myUserDetailsService;
}
public void setMyUserDetailsService(SocialUserDetailsService myUserDetailsService) {
this.myUserDetailsService = myUserDetailsService;
}
public UsersConnectionRepository getUsersConnectionRepository() {
return usersConnectionRepository;
}
public void setUsersConnectionRepository(UsersConnectionRepository usersConnectionRepository) {
this.usersConnectionRepository = usersConnectionRepository;
}
}
编写OpenIdAuthenticationFilter
/**
* @author lvhaibao
* @description
* @date 2019/1/8 0008 14:52
*/
public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String openIdParameter = "openId";
private String providerIdParameter = "providerId";
/**
* 是否只处理post请求
*/
private boolean postOnly = true;
public OpenIdAuthenticationFilter(){
//要拦截的请求
super(new AntPathRequestMatcher("/authentication/openid", "POST"));
}
@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 {
String openId = this.obtainOpenId(request);
String providerId = this.obtainProvider(request);
if (openId == null) {
openId = "";
}
if(providerId == null){
providerId = "";
}
openId = openId.trim();
providerId = providerId.trim();
//把手机号传进SmsCodeAuthenticationToken
OpenIdAuthenticationToken authRequest = new OpenIdAuthenticationToken(openId, providerId);
this.setDetails(request, authRequest);
//调用AuthenticationManager
return this.getAuthenticationManager().authenticate(authRequest);
}
}
/**
* 获取openId
*
* @param request request
* @return String
*/
private String obtainOpenId(HttpServletRequest request) {
return request.getParameter(this.openIdParameter);
}
private String obtainProvider(HttpServletRequest request){
return request.getParameter(this.providerIdParameter);
}
private 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 final String getOpenIdParameter() {
return this.openIdParameter;
}
public String getProviderIdParameter() {
return providerIdParameter;
}
public void setProviderIdParameter(String providerIdParameter) {
this.providerIdParameter = providerIdParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
}
编写配置
/**
* @author lvhaibao
* @description
* @date 2019/1/8 0008 14:54
*/
@Component
public class OpenIdAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private SocialUserDetailsService userDetailsService;
@Autowired
private UsersConnectionRepository connectionRepository;
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
@Bean
public AppSignUpUtils appSignUpUtils(){
return new AppSignUpUtils(redisTemplate, connectionRepository, connectionFactoryLocator);
}
@Override
public void configure(HttpSecurity http) throws Exception {
OpenIdAuthenticationFilter openIdAuthenticationFilter = new OpenIdAuthenticationFilter();
//设置AuthenticationManager
openIdAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
//设置成功失败处理器
openIdAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
//设置provider
OpenIdAuthenticationProvider openIdAuthenticationProvider = new OpenIdAuthenticationProvider();
openIdAuthenticationProvider.setMyUserDetailsService(userDetailsService);
openIdAuthenticationProvider.setUsersConnectionRepository(connectionRepository);
http.authenticationProvider(openIdAuthenticationProvider)
.addFilterAfter(openIdAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
在websecurity添加配置
/**
* @author lvhaibao
* @description 浏览器配置
* @date 2018/12/25 0025 10:53
*/
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private VcodeManager vcodeManager;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
@Autowired
private OpenIdAuthenticationConfig openIdAuthenticationConfig;
@Autowired
private SpringSocialConfigurer mySocialSecurityConfig;
@Autowired
private DataSource dataSource;
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
// @Override
// @Bean
// public AuthenticationManager authenticationManagerBean() throws Exception {
// return super.authenticationManagerBean();
// }
/**
* 生成记得我的token
*
* @return
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
//使用jdbc来存储
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
//设置数据源
tokenRepository.setDataSource(dataSource);
//当为true的时候就会自动创建表
//tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SmsCodeFilter smsCodeFilter = new SmsCodeFilter(vcodeManager);
smsCodeFilter.setSecurityProperties(securityProperties);
smsCodeFilter.afterPropertiesSet();
http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
//表单登录,loginPage为登录请求的url,loginProcessingUrl为表单登录处理的URL
.formLogin().loginPage(FromLoginConstant.LOGIN_PAGE).loginProcessingUrl(FromLoginConstant.LOGIN_PROCESSING_URL)
//登录成功之后的处理
.successHandler(myAuthenticationSuccessHandler)
//允许访问
.and().authorizeRequests().antMatchers(
FromLoginConstant.LOGIN_PROCESSING_URL,
FromLoginConstant.LOGIN_PAGE,
securityProperties.getOauthLogin().getOauthLogin(),
securityProperties.getOauthLogin().getOauthGrant(),
"/myLogout",
"/code/sms")
// "/oauth/**")
.permitAll().anyRequest().authenticated()
//禁用跨站伪造
.and().csrf().disable()
//短信验证码配置
.apply(smsCodeAuthenticationSecurityConfig)
//社交登录
.and().apply(mySocialSecurityConfig)
//openID登录
.and().apply(openIdAuthenticationConfig);
}
}
测试
使用http工具post请求:http://www.pinzhi365.com/authentication/openid。 如图所示:
即可返回token。
项目源码
https://gitee.com/lvhaibao/spring-lhbauth/tree/f15183a593f64d6a9494f1800f9b8ce4498111ae/