JustAuth-第三方登录组件

1、新增依赖

<dependency>
   <groupId>me.zhyd.oauth</groupId>
   <artifactId>JustAuth</artifactId>
   <version>1.16.5</version>
</dependency>

2、前端示例

<template>
  <div class="social-signup-container">
    <div class="sign-btn" @click="thirdLoginClick('wechat')">
      <span class="wx-svg-container"><svg-icon icon-class="wechat" class="icon"/></span>
      微信
    </div>
    <div class="sign-btn" @click="thirdLoginClick('qq')">
      <span class="qq-svg-container"><svg-icon icon-class="qq" class="icon"/></span>
      QQ
    </div>
    <div class="sign-btn" @click="thirdLoginClick('gitee')">
      <span class="gitee-svg-container"><svg-icon icon-class="gitee" class="icon"/></span>
      Gitee
    </div>
  </div>
</template>
<script>
export default {
name: 'SocialSignin',
methods: {
   // 请求后台获取跳转路径
thirdLoginClick(source) {
this.$store.dispatch('user/thirdLogin', {source: source}).then(() => {
}).catch(() => {
})
}
}
}
</script>
  // 第三方登录
  thirdLogin({commit}, userInfo) {
    const {source} = userInfo
    return new Promise((resolve, reject) => {
      thirdLogin({source: source.trim()}).then(response => {
        console.log("第三方登录返回", response)
        if (response.data.code === "200") {
          window.location.href = response.data.url
        } else {
          Message.warning(response.data.msg);
        }
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  }
export function thirdLogin(data) {
  return request({
    url: '/login/render',
    method: 'post',
    params: {source: data.source}
  })
}

3、后端示例

获取跳转路径
import com.mxy.common.core.utils.ServiceResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;

/**
 * 第三方登录认证
 */
@RestController
@RequestMapping("/api/login")
@Api(value = "第三方登录相关接口", tags = {"第三方登录相关接口"})
@Slf4j
public class AuthRestApi {

    @Resource
    private AuthUtil authUtil;

    @ApiOperation(value = "系统认证", notes = "系统认证")
    @RequestMapping("/render")
    public String renderAuth(String source) {
        log.info("进入第三方认证:" + source);
        Map<String, String> map = new HashMap<>();
        AuthRequest authRequest = authUtil.getAuthRequest(source);
        if (authRequest == null) {
            map.put("code", "201");
            map.put("msg", "系统未开启该登录方式");
            return ServiceResult.success(map);
        }
        String token = AuthStateUtils.createState();
        String authorizeUrl = authRequest.authorize(token);
        log.info("获取返回url:" + authorizeUrl);
        map.put("code", "200");
        map.put("url", authorizeUrl);
        return ServiceResult.success(map);
    }

}

拦截登录回调接口(ThirdPartyAuthenticationFilter)

import me.zhyd.oauth.model.AuthCallback;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 第三方登录-拦截器
 * 拼接入参
 */
public class ThirdPartyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public static String EXTEND_LOGIN_URL = "/api/login/callback/**";

    private boolean getOnly = true;

    /**
     * 表示这个 Filter 拦截 /api/login/callback/** 接口
     */
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(EXTEND_LOGIN_URL, "GET");

    public ThirdPartyAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.getOnly && !"GET".equals(request.getMethod())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            ThirdPartyAuthenticationToken authRequest = new ThirdPartyAuthenticationToken(getSourceType(request), getCallback(request));
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    protected void setDetails(HttpServletRequest request, ThirdPartyAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    /**
     * 组装请求
     */
    private AuthCallback getCallback(HttpServletRequest request) {
        return AuthCallback.builder()
                .code(request.getParameter("code"))
                .auth_code(request.getParameter("auth_code"))
                .authorization_code(request.getParameter("authorization_code"))
                .oauth_token(request.getParameter("oauth_token"))
                .state(request.getParameter("state"))
                .oauth_verifier(request.getParameter("oauth_verifier"))
                .build();
    }

    /**
     * 判断-登录系统类型
     */
    private String getSourceType(HttpServletRequest request) {
        String uri = request.getRequestURI();
        int common = EXTEND_LOGIN_URL.length() - 2;
        int start = uri.indexOf(EXTEND_LOGIN_URL.substring(0, common));
        return uri.substring(start + common);
    }

}
封装用户信息(ThirdPartyAuthenticationToken)
import me.zhyd.oauth.model.AuthCallback;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * 模仿 UsernamePasswordAuthenticationToken
 * 封装用户信息
 */
public class ThirdPartyAuthenticationToken extends AbstractAuthenticationToken {

    /**
     * 认证返回
     */
    private AuthCallback callback;
    /**
     * 登录类型
     */
    private String source;
    /**
     * 用户实体
     */
    private Object principal;
    /**
     * 用户id
     */
    private Object credentials;

    public ThirdPartyAuthenticationToken(String source, AuthCallback callback) {
        super(null);
        this.source = source;
        this.callback = callback;
        setAuthenticated(false);
    }

    public ThirdPartyAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }

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

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

    public Object getCallback() {
        return callback;
    }

    public Object getSource() {
        return source;
    }
}
第三方统一登录认证逻辑(ThirdPartyAuthenticationProvider)
package com.mxy.security.justauth;

import com.alibaba.fastjson.JSONObject;
import com.mxy.common.core.constant.Constants;
import com.mxy.common.core.entity.SelfUserEntity;
import com.mxy.common.core.entity.SysRole;
import com.mxy.common.core.entity.SysUser;
import com.mxy.common.core.entity.SysUserRole;
import com.mxy.common.core.utils.IPUtils;
import com.mxy.common.core.utils.RedisUtil;
import com.mxy.common.core.utils.ServletUtils;
import com.mxy.common.log.enums.OperType;
import com.mxy.security.security.service.SelfUserDetailsService;
import com.mxy.system.service.SysUserService;
import com.mxy.system.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * 第三方统一登录认证逻辑
 */
@Slf4j
@Component
public class ThirdPartyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private SelfUserDetailsService selfUserDetailsService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private AuthUtil authUtil;

    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    private Random random = new Random();


    /**
     * 第三方统一登录认证逻辑
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        ThirdPartyAuthenticationToken token = (ThirdPartyAuthenticationToken) authentication;
        String source = (String) token.getSource();
        AuthCallback callback = (AuthCallback) token.getCallback();
        log.info("------------进入" + source + "认证逻辑, callback params:" + JSONObject.toJSONString(callback));
        AuthRequest authRequest = authUtil.getAuthRequest(source);
        AuthResponse response = authRequest.login(callback);
        if (response.getCode() == 5000) {
            // 认证失败
            throw new BadCredentialsException(source + "认证失败");
        }
        AuthUser authUser = (AuthUser) response.getData();
        log.info("------------认证用户:{}", authUser);
        // 根据 uuid 查询用户信息
        SelfUserEntity userInfo = selfUserDetailsService.getUserInfoByUuid(authUser.getUuid());
        if (userInfo == null) {
            // 自动注册
            userInfo = doRegister(authUser);
        }
        if (Constants.USER_STATE_TWO.equals(userInfo.getStatus())) {
            LogUtil.saveLog("该账号已冻结[" + userInfo.getRelName() + "]", 99);
            throw new LockedException("该账号已冻结");
        }
        // 角色集合
        Set<GrantedAuthority> authorities = new HashSet<>();
        // 查询用户角色
        List<SysRole> sysRoleList = sysUserService.selectSysRoleByUserId(userInfo.getUserId());
        for (SysRole sysRole : sysRoleList) {
            authorities.add(new SimpleGrantedAuthority(sysRole.getRoleKey()));
        }
        userInfo.setAuthorities(authorities);
        ThirdPartyAuthenticationToken authenticationResult = new ThirdPartyAuthenticationToken(userInfo, userInfo.getUserId(), userInfo.getAuthorities());
        authenticationResult.setDetails(token.getDetails());
        return authenticationResult;
    }

    /**
     * 账号注册
     **/
    public SelfUserEntity doRegister(AuthUser authUser) {
        SelfUserEntity selfUser = new SelfUserEntity();
        SysUser sysUser = new SysUser();
        sysUser.setNickName(authUser.getNickname());
        sysUser.setUsername(authUser.getSource() + (random.nextInt(89999999) + 10000000));
        String password = String.valueOf(random.nextInt(899999) + 100000);
        sysUser.setPassword(bCryptPasswordEncoder.encode(password));
        sysUser.setAvatar(authUser.getAvatar());
        sysUser.setRegistrationType(authUser.getSource());
        sysUser.setUuid(authUser.getUuid());
        sysUser.setLoginCount(0);
        sysUser.setIpSource(IPUtils.getClientIp(Objects.requireNonNull(ServletUtils.getRequest())));
        // 2-男
        sysUser.setSex("2".equals(authUser.getRawUserInfo().getString("gender_type")) ? "0" : "1");
        sysUser.setCreateUser("system");
        sysUser.setRemark(authUser.getSource() + "首次注册默认密码为:" + password);
        sysUser.setLoginDate(new Date());
        sysUser.setUserType("2");
        sysUser.insert();

        // 新增用户角色关系
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setUserId(sysUser.getUserId());
        // 游客
        sysUserRole.setRoleId("2");
        sysUserRole.insert();
        BeanUtils.copyProperties(sysUser, selfUser);
        selfUser.setRelName(sysUser.getNickName());
        LogUtil.saveNoLoginLog("账号注册(" + authUser.getSource() + ")", JSONObject.toJSONString(sysUser), OperType.REGISTRATION.ordinal());
        return selfUser;
    }


    /**
     * 判断是上面 authenticate 方法的 authentication 参数,是哪种类型
     * Authentication 是个接口,实现类有很多,目前我们最熟悉的就是 ThirdPartyAuthenticationToken、UsernamePasswordAuthenticationToken
     * 很明显,我们只支持 ThirdPartyAuthenticationToken,因为它封装的是TOKEN OPENID
     *
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return (ThirdPartyAuthenticationToken.class.isAssignableFrom(authentication));
    }
}

工具类

import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.request.AuthGiteeRequest;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class AuthUtil {

    @Value("${justauth.qq.clientId}")
    private String qqClientId;

    @Value("${justauth.qq.clientSecret}")
    private String qqClientSecret;

    @Value("${justauth.qq.redirectUri}")
    private String qqRedirectUri;

    @Value("${justauth.gitee.clientId}")
    private String giteeClientId;

    @Value("${justauth.gitee.clientSecret}")
    private String giteeClientSecret;

    @Value("${justauth.gitee.redirectUri}")
    private String giteeRedirectUri;

    /**
     * 鉴权
     */
    public AuthRequest getAuthRequest(String source) {
        AuthRequest authRequest = null;
        switch (source) {
            case "qq":
                authRequest = new AuthQqRequest(AuthConfig.builder()
                        .clientId(qqClientId)
                        .clientSecret(qqClientSecret)
                        .redirectUri(qqRedirectUri)
                        .build());
                break;
            case "gitee":
                authRequest = new AuthGiteeRequest(AuthConfig.builder()
                        .clientId(giteeClientId)
                        .clientSecret(giteeClientSecret)
                        .redirectUri(giteeRedirectUri)
                        .build());
                break;
            default:
                break;
        }
        return authRequest;
    }

}
# 第三方登录秘钥
justauth:
  qq:
    clientId: xxxxxxxxxx
    clientSecret: xxxxxxxxxxxx
    redirectUri: http://xxxxxxxxxx/api/login/callback/qq
  gitee:
    clientId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    clientSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    redirectUri: http://xxxxxxxxxx/api/login/callback/gitee

 

posted @ 2022-12-09 13:27  许孟  阅读(222)  评论(0编辑  收藏  举报