APP邮箱登录,Facebook,Google以及appId如何登录手机App

相信大家都了解过Oauth2.0以及SPring Security在安全认证和登录上的作用,很多会使用oauth2,0的grant_type来进行第三方授权登录。今天为大家讲解新的手机app第三方登录。
 1.WebSecurityConfigurerAdapter的核心作用是简化 Spring Security 的配置流程,通过继承该类并覆写特定方法,开发者可以轻松定制应用的安全规则,无需从零实现复杂的安全配置接口。具体来说,它的主要功能包括:

配置 HTTP 请求的访问控制
例如:指定哪些 URL 需要登录后访问、哪些 URL 可以匿名访问、哪些 URL 需要特定角色权限等。
配置用户认证方式
例如:基于内存的用户存储、基于数据库的用户查询(结合UserDetailsService)、OAuth2 第三方登录等。
配置安全相关的细节
例如:CSRF 防护开关、会话管理策略(如会话创建、过期处理)、自定义登录页 / 登出页、异常处理(如 403 权限不足页面)等。
常用方法
继承WebSecurityConfigurerAdapter后,通常需要覆写以下两个核心方法:

configure(HttpSecurity http)
用于配置 HTTP 请求的安全规则,是最常用的方法。例如:
java
运行
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 配置请求授权
.antMatchers("/public/").permitAll() // 公开路径允许匿名访问
.antMatchers("/admin/
").hasRole("ADMIN") // 管理员路径需要ADMIN角色
.anyRequest().authenticated() // 其他所有请求需要认证
.and()
.formLogin() // 配置表单登录
.loginPage("/login") // 自定义登录页
.permitAll() // 登录页允许匿名访问
.and()
.logout() // 配置登出
.permitAll();
}

configure(AuthenticationManagerBuilder auth)
用于配置用户认证的数据源(即 “谁可以登录”)。例如:
java
运行
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 基于内存的用户配置(仅用于测试)
auth.inMemoryAuthentication()
.withUser("user").password("{noop}123456").roles("USER")
.and()
.withUser("admin").password("{noop}123456").roles("ADMIN");
}

注:{noop}表示不加密(仅测试用),实际开发需使用密码编码器(如BCryptPasswordEncoder)。
注意事项
已过时:在 Spring Security 5.7.0 及以上版本中,WebSecurityConfigurerAdapter已被标记为过时(deprecated)。官方推荐使用组件式配置(通过@Bean定义SecurityFilterChain、UserDetailsService等)替代继承该类的方式。
例如,替代configure(HttpSecurity http)的新方式:
java
运行
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
return http.build();
}

核心思想不变:无论是否使用WebSecurityConfigurerAdapter,Spring Security 的配置核心都是定义 “哪些资源需要保护”“谁能访问这些资源”“如何验证身份”,只是实现方式从 “继承类覆写方法” 变为 “直接定义 Bean”。
2.了解完springsecurity开始进入手机第三方登录流程
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
http
.formLogin()
.loginPage("/token/login")
.loginProcessingUrl("/token/form")
.failureHandler(authenticationFailureHandler())
.and()
.authorizeRequests()
.antMatchers(
"/v1/",
"/token/
",
"/token/",
"/actuator/
",
"/mobile/**").permitAll() // 以“/token”,“/actuator”,“/mobile”开头的,不需要用户进行身份验证
.anyRequest().authenticated()
.and().csrf().disable()
.apply(mobileSecurityConfigurer());
}
@Bean
public MobileSecurityConfigurer mobileSecurityConfigurer() {
return new MobileSecurityConfigurer();
}
@Getter
@Setter
public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Resource
private ObjectMapper objectMapper;
@Resource
private AuthenticationEventPublisher defaultAuthenticationEventPublisher;
@Resource
private AuthenticationSuccessHandler mobileLoginSuccessHandler;
@Resource
private PigxUserDetailsService userDetailsService;

@Override
public void configure(HttpSecurity http) {
    // 创建 MobileAuthenticationFilter 实例
	MobileAuthenticationFilter mobileAuthenticationFilter = new MobileAuthenticationFilter();
    // 设置身份验证管理器,用于处理身份验证
	mobileAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    // 设置身份验证成功处理程序,用于成功登录后的处理
	mobileAuthenticationFilter.setAuthenticationSuccessHandler(mobileLoginSuccessHandler);
    // 设置事件发布器,通常用于发布身份验证事件
	mobileAuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
    // 设置身份验证入口点,用于处理身份验证失败的情况
    mobileAuthenticationFilter.setAuthenticationEntryPoint(new ResourceAuthExceptionEntryPoint(objectMapper));
    // 创建 MobileAuthenticationProvider 实例
	MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider();
    // 设置用户详细信息服务,用于获取用户信息以进行身份验证
    mobileAuthenticationProvider.setUserDetailsService(userDetailsService);
    // 将移动设备身份验证提供者添加到 Spring Security 配置
    http.authenticationProvider(mobileAuthenticationProvider)
            // 将移动设备身份验证过滤器添加到 Spring Security 配置,并放在用户名密码身份验证过滤器之后
			.addFilterAfter(mobileAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}

}

/*

  • Copyright (c) 2018-2025, lengleng All rights reserved.
  • Redistribution and use in source and binary forms, with or without
  • modification, are permitted provided that the following conditions are met:
  • Redistributions of source code must retain the above copyright notice,
  • this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright
  • notice, this list of conditions and the following disclaimer in the
  • documentation and/or other materials provided with the distribution.
  • Neither the name of the pig4cloud.com developer nor the names of its
  • contributors may be used to endorse or promote products derived from
  • this software without specific prior written permission.
  • Author: lengleng (wangiegie@gmail.com)
    */

package pro.nbbt.xulian.common.security.mobile;

import cn.hutool.http.HttpUtil;
import pro.nbbt.xulian.common.core.constant.SecurityConstants;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import pro.nbbt.xulian.common.core.util.http.HttpKit;
import pro.nbbt.xulian.common.security.service.PigxUser;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**

  • @author lengleng

  • @date 2018/1/9

  • 手机号登录验证filter
    */
    public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    private static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
    @Getter
    @Setter
    private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
    @Getter
    @Setter
    private boolean postOnly = true;
    @Getter
    @Setter
    private AuthenticationEventPublisher eventPublisher;
    @Getter
    @Setter
    private AuthenticationEntryPoint authenticationEntryPoint;

    public MobileAuthenticationFilter() {
    super(new AntPathRequestMatcher(SecurityConstants.MOBILE_TOKEN_URL, "POST"));
    }

    @Override
    @SneakyThrows
    public Authentication attemptAuthentication(HttpServletRequest request,
    HttpServletResponse response) {
    if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
    throw new AuthenticationServiceException(
    "Authentication method not supported: " + request.getMethod());
    }

// String mobile = obtainMobile(request);
//
// if (mobile == null) {
// mobile = "";
// }
//
// mobile = mobile.trim();

	String authUser = HttpKit.getRequestParametersJSON(request.getHeader("app")).toString();
	logger.info("Authentication : " + authUser);


	MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(authUser);

	setDetails(request, mobileAuthenticationToken);

	Authentication authResult = null;
	try {
		authResult = this.getAuthenticationManager().authenticate(mobileAuthenticationToken);
		logger.info("Authentication request authResult: " + authResult);

		SecurityContextHolder.getContext().setAuthentication(authResult);

	} catch (Exception failed) {
		SecurityContextHolder.clearContext();
		logger.info("Authentication request failed: " + failed.getMessage());

		eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
				new PreAuthenticatedAuthenticationToken("access-token", "N/A"));

		try {
			authenticationEntryPoint.commence(request, response,
					new UsernameNotFoundException(failed.getMessage(), failed));
		} catch (Exception e) {
			logger.error("authenticationEntryPoint handle error:{}", failed);
		}
	}

	return authResult;
}

// private String obtainMobile(HttpServletRequest request) {
// return request.getParameter(mobileParameter);
// }

private void setDetails(HttpServletRequest request,
						MobileAuthenticationToken authRequest) {
	authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}

}

/*

  • Copyright (c) 2018-2025, lengleng All rights reserved.
  • Redistribution and use in source and binary forms, with or without
  • modification, are permitted provided that the following conditions are met:
  • Redistributions of source code must retain the above copyright notice,
  • this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright
  • notice, this list of conditions and the following disclaimer in the
  • documentation and/or other materials provided with the distribution.
  • Neither the name of the pig4cloud.com developer nor the names of its
  • contributors may be used to endorse or promote products derived from
  • this software without specific prior written permission.
  • Author: lengleng (wangiegie@gmail.com)
    */

package pro.nbbt.xulian.common.security.mobile;

import org.springframework.security.core.context.SecurityContextHolder;
import pro.nbbt.xulian.common.security.component.PigxPreAuthenticationChecks;
import pro.nbbt.xulian.common.security.service.PigxUser;
import pro.nbbt.xulian.common.security.service.PigxUserDetailsService;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;

/**

  • @author lengleng

  • @date 2018/8/5

  • 手机登录校验逻辑

  • 验证码登录、社交登录
    */
    @Slf4j
    public class MobileAuthenticationProvider implements AuthenticationProvider {
    private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private UserDetailsChecker detailsChecker = new PigxPreAuthenticationChecks();

    @Getter
    @Setter
    private PigxUserDetailsService userDetailsService;

    @Override
    @SneakyThrows
    public Authentication authenticate(Authentication authentication) {
    MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;

     String principal = mobileAuthenticationToken.getPrincipal().toString();
     UserDetails userDetails = userDetailsService.loadUserBySocial(principal);
     if (userDetails == null) {
     	log.debug("Authentication failed: no credentials provided");
    
     	throw new BadCredentialsException(messages.getMessage(
     			"AbstractUserDetailsAuthenticationProvider.noopBindAccount",
     			"Noop Bind Account"));
     }
     // 检查账号状态
     detailsChecker.check(userDetails);
    
     MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails, userDetails.getAuthorities());
     Authentication authentication2 = SecurityContextHolder.getContext().getAuthentication();
     if (authentication2 != null && authentication2.getPrincipal() instanceof PigxUser) {
     	PigxUser user = (PigxUser) authentication2.getPrincipal();
     	// 进行后续操作
     	log.info("authenticationToken信息:{}", user.getBindFlag());
     }
     log.info("authenticationToken信息2:{}", authenticationToken.getPrincipal());
    
     authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
     return authenticationToken;
    

    }

    @Override
    public boolean supports(Class<?> authentication) {
    return MobileAuthenticationToken.class.isAssignableFrom(authentication);
    }
    }
    /*

  • Copyright (c) 2018-2025, lengleng All rights reserved.

  • Redistribution and use in source and binary forms, with or without

  • modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice,

  • this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright

  • notice, this list of conditions and the following disclaimer in the

  • documentation and/or other materials provided with the distribution.

  • Neither the name of the pig4cloud.com developer nor the names of its

  • contributors may be used to endorse or promote products derived from

  • this software without specific prior written permission.

  • Author: lengleng (wangiegie@gmail.com)
    */

package pro.nbbt.xulian.admin.handler;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import pro.nbbt.xulian.admin.api.dto.AuthUserDTO;
import pro.nbbt.xulian.admin.api.dto.UserInfo;
import pro.nbbt.xulian.admin.api.entity.SysUser;
import pro.nbbt.xulian.admin.service.SysUserService;
import pro.nbbt.xulian.common.core.constant.CommonConstants;
import pro.nbbt.xulian.common.core.util.http.HttpKit;

import java.util.Objects;

/**

  • @author bazinga

  • @date 2020/11/05
    */
    @Slf4j
    @Component("GOOGLE_APP")
    @AllArgsConstructor
    public class GoogleAppLoginHandler extends AbstractLoginHandler {
    private final SysUserService sysUserService;

    /**

    • 手机原生登录 传入的 code 就是 第三方账号ID
    • @param code
    • @return
      */
      @Override
      public String identify(String code) {
      return code;
      }

    /**

    • loginStr 获取用户信息
    • @param loginStr
    • @return
      */
      @Override
      public UserInfo info(String loginStr) {
      return null;
      }

    /**

    • authUser 获取用户信息

    • @param authUser

    • @return
      */
      @Override
      public UserInfo infoApp(AuthUserDTO authUser) {
      log.info("<><><><> 当前Google登录邮箱 <><><><> {}", authUser.getUsername());
      log.info("<><><><> 当前Google邮箱 uuid <><><><> {}", authUser.getUuid());
      // 供应商标记 新app会在header中添加供应商标记
      String appType = authUser.getAppType();
      log.info("当前Google登录时的appType为:{}", appType);
      // log.info("用户authUser的基本信息=={}",authUser.toString());
      SysUser user = sysUserService.getOne(Wrappers.query().lambda()
      .eq(SysUser::getGoogleId, authUser.getUuid())
      .eq(SysUser::getUserLevelCode, CommonConstants.USER_LEVEL_APP) // 目前只有app有第三方登录
      .eq(SysUser::getClusterType, CommonConstants.CLUSTER_TYPE));
      if (user == null) {
      // 通过username 获取用户信息
      SysUser user1 = null;
      if (StrUtil.isNotBlank(authUser.getUsername())) {
      user1 = sysUserService.getOne(Wrappers.query().lambda()
      .eq(SysUser::getUsername, authUser.getUsername())
      .eq(SysUser::getUserLevelCode, CommonConstants.USER_LEVEL_APP) // 目前只有app有第三方登录
      .eq(SysUser::getClusterType, CommonConstants.CLUSTER_TYPE));

       }
       if (Objects.nonNull(user1) && Objects.isNull(user1.getGoogleId())) {
           // 存在并且 第三方id 为空则修改
           user1.setPassword(null);
           user1.setGoogleId(authUser.getUuid());
           sysUserService.updateById(user1);
           return sysUserService.findUserInfo(user1);
       } else if (Objects.isNull(user1)) {
           if (StrUtil.isEmpty(authUser.getUuid())) {
               return null;
           }
           // 账号不存在
           user = new SysUser();
           user.setUsername(StrUtil.isNotBlank(authUser.getUsername()) ? authUser.getUsername() : UUID.fastUUID().toString().replace(StrUtil.DASHED, StrUtil.EMPTY));
           user.setGoogleId(authUser.getUuid());
           user.setPassword(UUID.fastUUID().toString());
           user.setNickname(authUser.getNickname());
           user.setAvatar(authUser.getAvatar());
           user.setLockFlag("0");
           user.setClusterType(CommonConstants.CLUSTER_TYPE);
           // 如果邮箱不为空,则设置为已绑定邮箱
      

// if (StrUtil.isNotBlank(authUser.getUsername())) {
// user.setBindFlag(1);
// }
user.setSupplierInfo(authUser.getAppType());

            sysUserService.registerForApp(user);
        } else {
            return null;
        }
        if (user != null && StrUtil.isNotBlank(authUser.getUsername()) && !StringUtils.equalsIgnoreCase(user.getUsername(), authUser.getUsername())) {
            user.setUsername(authUser.getUsername());
            sysUserService.updateById(user);
        }
    }

// if(user != null && StrUtil.isNotBlank(authUser.getUsername()) && !StringUtils.equalsIgnoreCase(user.getUsername(),authUser.getUsername())){
// user.setUsername(authUser.getUsername());
// sysUserService.updateById(user);
// }
log.info("当前Google登录时的user为:{}", user);

    return sysUserService.findUserInfo(user);
}

}
3.上述代码中核心在于
infoApp方法将googele账户,facebook,appid账户引入自有项目,创立自有app账号密码,前提是她登录使用的第三方账号密码能登录第三方平台。

posted @ 2025-08-01 10:47  kevinWwm  阅读(39)  评论(0)    收藏  举报