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账号密码,前提是她登录使用的第三方账号密码能登录第三方平台。
浙公网安备 33010602011771号