栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot 集成 微信绑定 微信登录

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot 集成 微信绑定 微信登录

SpringBoot 集成 微信登录

重写一个认证逻辑 实现AuthenticationProvider

import com.hzjtcl.commons.security.service.HzjtclUserDetailsService;
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;

@Slf4j
public class MobileAuthenticationProvider implements AuthenticationProvider {

	private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

	@Getter
	@Setter
	private HzjtclUserDetailsService 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());
		authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
		return authenticationToken;
	}

	@Override
	public boolean supports(Class authentication) {
		return MobileAuthenticationToken.class.isAssignableFrom(authentication);
	}

}
import lombok.SneakyThrows;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;

import java.util.Collection;


public class MobileAuthenticationToken extends AbstractAuthenticationToken {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	private final Object principal;

	public MobileAuthenticationToken(String mobile) {
		super(null);
		this.principal = mobile;
		setAuthenticated(false);
	}

	public MobileAuthenticationToken(Object principal, Collection authorities) {
		super(authorities);
		this.principal = principal;
		super.setAuthenticated(true);
	}

	@Override
	public Object getPrincipal() {
		return this.principal;
	}

	@Override
	public Object getCredentials() {
		return null;
	}

	@Override
	@SneakyThrows
	public void setAuthenticated(boolean isAuthenticated) {
		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();
	}

}

登录认证及获取用户信息

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;


public interface HzjtclUserDetailsService extends UserDetailsService {

	
	UserDetails loadUserBySocial(String social) throws UsernameNotFoundException;


	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException ;

}
import com.hzjtcl.commons.security.enums.UserStatusEnum;
import com.hzjtcl.commons.security.feign.AccountFeignClient;
import com.hzjtcl.commons.security.service.HzjtclUserDetailsService;
import com.hzjtcl.commons.security.user.UserDetail;
import com.hzjtcl.commons.tools.exception.ErrorCode;
import com.hzjtcl.commons.tools.exception.RenException;
import com.hzjtcl.commons.tools.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;


@Service
public class RenUserDetailsServiceImpl implements HzjtclUserDetailsService {
    @Autowired(required=false)
    private AccountFeignClient accountFeignClient;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Result result = accountFeignClient.getByUsername(username);
        UserDetail userDetail = result.getData();
        //账号不存在
        if(userDetail == null){
            throw new RenException(ErrorCode.ACCOUNT_NOT_EXIST);
        }

        //账号不可用
        if(userDetail.getStatus() == UserStatusEnum.DISABLE.value()){
            throw new RenException(ErrorCode.ACCOUNT_DISABLE);
        }

        return userDetail;
    }

    @Override
    public UserDetails loadUserBySocial(String social) throws UsernameNotFoundException {
        Result result = accountFeignClient.loadUserBySocial(social);
        UserDetail userDetail = result.getData();
        //账号不存在
        if(userDetail == null){
            return null;
        }

        //账号不可用
        if(userDetail.getStatus() == UserStatusEnum.DISABLE.value()){
            return null;
        }

        return userDetail;
    }
}
import com.hzjtcl.commons.security.feign.fallback.AccountFeignClientFallbackFactory;
import com.hzjtcl.commons.security.user.UserDetail;
import com.hzjtcl.commons.tools.constant.ServiceConstant;
import com.hzjtcl.commons.tools.utils.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;


@FeignClient(name = ServiceConstant.HZJTCL_ADMIN_SERVER, contextId = "AccountFeignClient", fallbackFactory = AccountFeignClientFallbackFactory.class)
public interface AccountFeignClient {

    
    @PostMapping("sys/user/getByUsername")
    Result getByUsername(@RequestParam("username") String username);


    
    @PostMapping("sys/user/socialinfo")
    Result loadUserBySocial(@RequestParam("inStr") String inStr);



}

controller 层自己去实现

service用户信息认证

    @Autowired
    private  Map loginHandlerMap;

 @Override
    public SysUserDTO getSocialinfoByinStr(String inStr) {
        String[] inStrs = inStr.split(StringPool.AT);
        String type = inStrs[0];
        String loginStr = inStrs[1];
        // 通过前端传递来的参数在map中获取  type为解析出的类的名称 通过@Component注解获取对应类
        return loginHandlerMap.get(type).handle(loginStr);
    }

handler 处理类

public interface LoginHandler {

	
	Boolean check(String loginStr);

	
	String identify(String loginStr);

	
	SysUserDTO info(String identify);

	
	SysUserDTO handle(String loginStr);

}
public abstract class AbstractLoginHandler implements LoginHandler {

	
	@Override
	public Boolean check(String loginStr) {
		return true;
	}

	
	@Override
	public SysUserDTO handle(String loginStr) {
		if (!check(loginStr)) {
			return null;
		}

		String identify = identify(loginStr);
		return info(identify);
	}

}

mport com.hzjtcl.dto.SysUserDTO;
import com.hzjtcl.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Slf4j
@Component("SMS")
public class SmsLoginHandler extends AbstractLoginHandler {

	@Autowired
	private SysUserService sysUserService;

	
	@Override
	public String identify(String mobile) {
		return mobile;
	}

	
	@Override
	public SysUserDTO info(String identify) {
		SysUserDTO user = sysUserService.getOneByinStr(identify);

		if (user == null) {
			log.info("手机号未注册:{}", identify);
			return null;
		}
		return user;
	}

}
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hzjtcl.commons.security.constant.SecurityConstants;
import com.hzjtcl.commons.tools.utils.ConvertUtils;
import com.hzjtcl.dao.SysSocialDetailsDao;
import com.hzjtcl.dao.SysUserDao;
import com.hzjtcl.dto.SysUserDTO;
import com.hzjtcl.entity.SysSocialDetailsEntity;
import com.hzjtcl.entity.SysUserEntity;
import com.hzjtcl.enums.LoginTypeEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component("WX")  // 通过前端传递来的参数在map中获取
@AllArgsConstructor
public class WeChatLoginHandler extends AbstractLoginHandler {


	private final SysUserDao sysUserDao;

	private final SysSocialDetailsDao sysSocialDetailsDao;

	
	@Override
	public String identify(String code) {
		SysSocialDetailsEntity condition = new SysSocialDetailsEntity();
		condition.setType(LoginTypeEnum.WECHAT.getType());
		SysSocialDetailsEntity socialDetails = sysSocialDetailsDao.selectOne(new QueryWrapper<>(condition));

		String url = String.format(SecurityConstants.WX_AUTHORIZATION_CODE_URL, socialDetails.getAppId(),
				socialDetails.getAppSecret(), code);
		String result = HttpUtil.get(url);
		log.debug("微信响应报文:{}", result);

		Object obj = JSONUtil.parseObj(result).get("openid");
		if (obj == null) {
			return "";
		}
		return obj.toString();
//		return code;
	}

	
	@Override
	public SysUserDTO info(String openId) {
		SysUserEntity user = sysUserDao.getOneByinStr(openId);

		if (user == null) {
			log.info("微信未绑定:{}", openId);
			return null;
		}
		return ConvertUtils.sourceToTarget(user, SysUserDTO.class);
	}

}

工具类

public interface SecurityConstants {

	
	String UTF8 = "UTF-8";
	
	Integer SUCCESS = 0;

	
	Integer FAIL = 1;


	
	String REFRESH_TOKEN = "refresh_token";

	
	int CODE_TIME = 60;

	
	String CODE_SIZE = "4";

	
	String ROLE = "ROLE_";

	
	String GCZJT_PREFIX = "gczjt_";

	
	String OAUTH_PREFIX = "oauth:";

	
	String GCZJT_LICENSE = "made by gczjt";

	
	String FROM_IN = "Y";

	
	String FROM = "from";

	
	String OAUTH_TOKEN_URL = "/oauth/token";

	
	String SMS_TOKEN_URL = "/mobile/token/sms";

	
	String SOCIAL_TOKEN_URL = "/mobile/token/social";

	
	String MOBILE_TOKEN_URL = "/mobile/token
	String WX_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"
			+ "?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

	
	String MINI_APP_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/jscode2session"
			+ "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";

	
	String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type="
			+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";

	
	String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";

	
	String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";

	
	String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";

	
	String BCRYPT = "{bcrypt}";

	
	String CLIENT_FIELDS = "client_id, CONCAt('{noop}',client_secret) as client_secret, resource_ids, scope, "
			+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
			+ "refresh_token_validity, additional_information, autoapprove";

	
	String base_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";

	
	String DEFAULT_SELECT_STATEMENT = base_FIND_STATEMENT + " where client_id = ? and del_flag = 0 and tenant_id = %s";

	
	String RESOURCE_SERVER_ConFIGURER = "resourceServerConfigurerAdapter";

	
	String CLIENT_CREDENTIALS = "client_credentials";

	
	String DETAILS_USER_ID = "id";

	
	String DETAILS_USERNAME = "username";

	
	String DETAILS_USERISFULL = "isFull";

	
	String DETAILS_USER = "user_info";

	
	String DETAILS_PHONE = "phone";

	
	String DETAILS_AVATAR = "avatar";

	
	String DETAILS_DEPT_ID = "deptId";

	
	String DETAILS_TENANT_ID = "tenantId";

	
	String DETAILS_LICENSE = "license";

	
	String ACTIVE = "active";

	
	String AES = "aes";

}

微信绑定

import com.hzjtcl.commons.tools.utils.Result;
import com.hzjtcl.service.SysSocialDetailsService;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/social")
@AllArgsConstructor
@Api(value = "social", tags = "三方账号管理模块")
public class SysSocialDetailsController {

	private final SysSocialDetailsService sysSocialDetailsService;


	
	@PostMapping("/bind")
	public Result bindSocial(String state, String code) {
		Boolean aBoolean = sysSocialDetailsService.bindSocial(state, code);
		if (aBoolean){
			return new Result().ok(true);
		}else{
			return new Result().error("绑定失败!");
		}

	}

}
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.hzjtcl.commons.mybatis.service.baseService;
import com.hzjtcl.entity.SysSocialDetailsEntity;


public interface SysSocialDetailsService extends baseService {

	
	Boolean bindSocial(String state, String code);

}

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.hzjtcl.commons.mybatis.service.impl.baseServiceImpl;
import com.hzjtcl.commons.security.user.SecurityUser;
import com.hzjtcl.dao.SysSocialDetailsDao;
import com.hzjtcl.entity.SysSocialDetailsEntity;
import com.hzjtcl.entity.SysUserEntity;
import com.hzjtcl.enums.CacheConstants;
import com.hzjtcl.enums.LoginTypeEnum;
import com.hzjtcl.handler.LoginHandler;
import com.hzjtcl.service.SysSocialDetailsService;
import com.hzjtcl.service.SysUserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;

import java.util.Map;


@Slf4j
@AllArgsConstructor
@Service
public class SysSocialDetailsServiceImpl extends baseServiceImpl
		implements SysSocialDetailsService {

	private final Map loginHandlerMap;

	private final CacheManager cacheManager;

	private final SysUserService sysUserService;

	
	@Override
	public Boolean bindSocial(String type, String code) {
		LoginHandler loginHandler = loginHandlerMap.get(type);
		String identify = loginHandler.identify(code);
		if (StringUtils.isNotBlank(identify)) {
			SysUserEntity user = sysUserService.selectById(SecurityUser.getUserId());


//		if (LoginTypeEnum.GITEE.getType().equals(type)) {
//			sysUser.setGiteeLogin(identify);
//		}
//		else if (LoginTypeEnum.OSC.getType().equals(type)) {
//			sysUser.setOscId(identify);
//		}
			if (LoginTypeEnum.WECHAT.getType().equals(type)) {
				user.setWxOpenid(identify);
			}
//		else if (LoginTypeEnum.QQ.getType().equals(type)) {
//			sysUser.setQqOpenid(identify);
//		}
			else if (LoginTypeEnum.MINI_APP.getType().equals(type)) {
				user.setMiniOpenid(identify);
			}

			sysUserService.updateById(user);
			// 更新緩存
			cacheManager.getCache(CacheConstants.USER_DETAILS).evict(user.getUsername());
			return Boolean.TRUE;
		}else{
			return Boolean.FALSE;
		}

	}
}

微信绑定和登录思路总结:
该系统使用微信登录之前 必须先进行微信绑定, 如果未绑定则提示用户进行微信绑定;
使用微信登录和微信绑定需要公司在微信开放平台进行注册,得到注册后的专属url和appId
前端通过appId和url进行微信接口访问得到 返回值 code
通过该code 和 登录类型 对用户进行 openID 的绑定,
openId需要 appId 和 code参数 再次对微信平台进行访问得到返回值 openId
信息绑定成功之后

登录时前端传入特定的参数, 进行参数的解析, 同样需要微信平台的code值
通过该code值和appId再次获取 openId ,通过openId来获取用户信息进行登录.

核心逻辑就是对微信接口访问得到code值和openId, 将openId与用户进行绑定,
登录时通过该openId获取用户信息.

前端代码
 wxlogin() {
      // ElMessage.success("测试中,敬请期待");

// 该路径为微信访问成功之后回调路径, 需在微信开放平台进行注册
      const redirect_uri = encodeURIComponent("http://oa.hebhzjt.com:7879/#/authredirect");
      // appid
      const appid = "wx549f52db7fc1da3b";
      // 该路由参数参照 微信开放平台api
      let url = `https://open.weixin.qq.com/connect/qrconnect?appid=${appid}&redirect_uri=${redirect_uri}&state=WX-LOGIN&response_type=code&scope=snsapi_login#wechat_redirect`;
      window.open(url);
    },
    // 登录接口调用
    onLoginBySocial(code) {
      baseService
        .post(
          "/auth/mobile/token/social",
          // 登录参数 code 为微信调用回调值 wx@用于后台通过 '@'进行参数解析获取登录类型
          { mobile: `WX@${code}`, grant_type: "mobile" },
          {
            "content-type": "application/x-www-form-urlencoded",
            Authorization: "Basic aHpqdGNsOmh6anRjbA=="
          }
        )
        .then((res) => {
          if (res.code === 0) {
            setCache(CacheToken, res, true);
            ElMessage.success(this.$t("ui.login.loginOk"));
            this.$router.push("/");
          } else {
            ElMessage.error(res.msg);
            this.onRefreshCode();
          }
        })
        .catch(() => {
          ElMessage.error("未绑定登录账号,请使用密码登录后绑定");
          this.loading = false;
        });
    },
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/732694.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号