若依实现第三方登录,史上最全保姆级教程

第一步:去申请对应平台的appid

1.1:登录Gitee

1.2:点击头像->点击设置

若依实现第三方登录,史上最全保姆级教程_第1张图片

1.3:点击第三方应用->点击创建应用

image.png #90%

1.4:填写基本信息,参照我的这个,应用回调地址我写了两个,一个本地测试用,一个发布到环境上使用,自己测试的时候写一个就行了,这个是第三方授权后的回调地址

若依实现第三方登录,史上最全保姆级教程_第2张图片

第二步:基本环境搭建

2.1:需要引入的依赖,在ruoyi-common下引入依赖


    org.apache.httpcomponents
    httpclient

2.2:数据表创建,为了以后可以拓展其他平台,我们创建第三方授权表

create table sys_user_third_account(
    id                   bigint auto_increment
        primary key,
    user_id              bigint                  null comment '对应sys_user的用户id',
    third_unique_account varchar(100)            null comment '第三方唯一用户id,可以是giteeId/QQ的openid,或苹果id,',
    third_unique_name    varchar(30)             null comment '第三方用户名',
    third_unique_avatar  varchar(300)            null comment '第三方用户头像url',
    bind_type            varchar(5)              null comment '标识第三方类型,1.gitee_id',
    bind_flag            varchar(2)  default '0' null comment '标识是否绑定:值集(user_third_status)',
    bind_date            datetime                null comment '绑定时间',
    create_by            varchar(64) default ''  null comment '创建者',
    create_time          datetime                null comment '创建时间',
    update_by            varchar(64) default ''  null comment '更新者',
    update_time          datetime                null comment '更新时间',
    del_flag             char        default '0' null comment '删除标志(0代表存在 2代表删除)'
)  comment '第三方账户绑定' charset = utf8mb4;

2.3:第三步,创建对应的实体类,mapper,service,这里可以直接通过若依的代码生产器进行生成,我这里也给你提供好了

2.3.1:实体类

package com.ruoyi.system.domain;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import java.util.Date;

/**
 * @author xiao_he
 */
public class SysUserThirdAccount extends BaseEntity {
    private static final long serialVersionUID = 1L;

    /**
     * $column.columnComment
     */
    private Long id;

    /**
     * 对应sys_user的用户id
     */
    private Long userId;

    /**
     * 第三方唯一用户id,可以是微信的openid,可以是QQ的openid,抑或苹果id,
     */
    private String thirdUniqueAccount;

    /**
     * 第三方用户名
     */
    private String thirdUniqueName;

    /**
     * 第三方用户头像url
     */
    private String thirdUniqueAvatar;

    /**
     * 标识第三方类型,1.gitee_id
     */
    private String bindType;

    /**
     * 标识是否绑定,1绑定,2解绑
     */
    private String bindFlag;

    /**
     * 绑定时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date bindDate;

    /**
     * 删除标志(0代表存在 2代表删除)
     */
    private String delFlag;

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Long getUserId() {
        return userId;
    }

    public void setThirdUniqueAccount(String thirdUniqueAccount) {
        this.thirdUniqueAccount = thirdUniqueAccount;
    }

    public String getThirdUniqueAccount() {
        return thirdUniqueAccount;
    }

    public void setThirdUniqueName(String thirdUniqueName) {
        this.thirdUniqueName = thirdUniqueName;
    }

    public String getThirdUniqueName() {
        return thirdUniqueName;
    }

    public void setThirdUniqueAvatar(String thirdUniqueAvatar) {
        this.thirdUniqueAvatar = thirdUniqueAvatar;
    }

    public String getThirdUniqueAvatar() {
        return thirdUniqueAvatar;
    }

    public void setBindType(String bindType) {
        this.bindType = bindType;
    }

    public String getBindType() {
        return bindType;
    }

    public void setBindFlag(String bindFlag) {
        this.bindFlag = bindFlag;
    }

    public String getBindFlag() {
        return bindFlag;
    }

    public void setBindDate(Date bindDate) {
        this.bindDate = bindDate;
    }

    public Date getBindDate() {
        return bindDate;
    }

    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }

    public String getDelFlag() {
        return delFlag;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("id", getId())
                .append("userId", getUserId())
                .append("thirdUniqueAccount", getThirdUniqueAccount())
                .append("thirdUniqueName", getThirdUniqueName())
                .append("thirdUniqueAvatar", getThirdUniqueAvatar())
                .append("bindType", getBindType())
                .append("bindFlag", getBindFlag())
                .append("bindDate", getBindDate())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("delFlag", getDelFlag())
                .toString();
    }
}

2.3.2:mapper.java

package com.ruoyi.system.mapper;

import com.ruoyi.system.domain.SysUserThirdAccount;

import java.util.List;

/**
 * @author xiao_he
 */
public interface SysUserThirdAccountMapper {
    /**
     * 查询第三方账户绑定
     *
     * @param id 第三方账户绑定主键
     * @return 第三方账户绑定
     */
    public SysUserThirdAccount selectSysUserThirdAccountById(Long id);

    /**
     * 查询第三方账户绑定列表
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 第三方账户绑定集合
     */
    public List selectSysUserThirdAccountList(SysUserThirdAccount sysUserThirdAccount);

    /**
     * 新增第三方账户绑定
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 结果
     */
    public int insertSysUserThirdAccount(SysUserThirdAccount sysUserThirdAccount);

    /**
     * 修改第三方账户绑定
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 结果
     */
    public int updateSysUserThirdAccount(SysUserThirdAccount sysUserThirdAccount);

    /**
     * 删除第三方账户绑定
     *
     * @param id 第三方账户绑定主键
     * @return 结果
     */
    public int deleteSysUserThirdAccountById(Long id);

    /**
     * 批量删除第三方账户绑定
     *
     * @param ids 需要删除的数据主键集合
     * @return 结果
     */
    public int deleteSysUserThirdAccountByIds(Long[] ids);
}

2.3.3:mapper对应的xml





    
        
        
        
        
        
        
        
        
        
        
        
        
        
    

    
        select id,
               user_id,
               third_unique_account,
               third_unique_name,
               third_unique_avatar,
               bind_type,
               bind_flag,
               bind_date,
               create_by,
               create_time,
               update_by,
               update_time,
               del_flag
        from sys_user_third_account
    

    

    

    
        insert into sys_user_third_account
        
            user_id,
            third_unique_account,
            third_unique_name,
            third_unique_avatar,
            bind_type,
            bind_flag,
            bind_date,
            create_by,
            create_time,
            update_by,
            update_time,
            del_flag,
        
        
            #{userId},
            #{thirdUniqueAccount},
            #{thirdUniqueName},
            #{thirdUniqueAvatar},
            #{bindType},
            #{bindFlag},
            #{bindDate},
            #{createBy},
            #{createTime},
            #{updateBy},
            #{updateTime},
            #{delFlag},
        
    

    
        update sys_user_third_account
        
            user_id = #{userId},
            third_unique_account = #{thirdUniqueAccount},
            third_unique_name = #{thirdUniqueName},
            third_unique_avatar = #{thirdUniqueAvatar},
            bind_type = #{bindType},
            bind_flag = #{bindFlag},
            bind_date = #{bindDate},
            create_by = #{createBy},
            create_time = #{createTime},
            update_by = #{updateBy},
            update_time = #{updateTime},
            del_flag = #{delFlag},
        
        where id = #{id}
    

    
        update sys_user_third_account
        set del_flag = '2'
        where id = #{id}
    

    
        update sys_user_third_account set del_flag = '2' where id in
        
            #{id}
        
    

2.3.4:service接口

package com.ruoyi.system.service;

import com.ruoyi.system.domain.SysUserThirdAccount;

import java.util.List;

/**
 * @author xiao_he
 */
public interface ISysUserThirdAccountService {
    /**
     * 查询第三方账户绑定
     *
     * @param id 第三方账户绑定主键
     * @return 第三方账户绑定
     */
    public SysUserThirdAccount selectSysUserThirdAccountById(Long id);

    /**
     * 查询第三方账户绑定列表
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 第三方账户绑定集合
     */
    public List selectSysUserThirdAccountList(SysUserThirdAccount sysUserThirdAccount);

    /**
     * 新增第三方账户绑定
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 结果
     */
    public int insertSysUserThirdAccount(SysUserThirdAccount sysUserThirdAccount);

    /**
     * 修改第三方账户绑定
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 结果
     */
    public int updateSysUserThirdAccount(SysUserThirdAccount sysUserThirdAccount);

    /**
     * 批量删除第三方账户绑定
     *
     * @param ids 需要删除的第三方账户绑定主键集合
     * @return 结果
     */
    public int deleteSysUserThirdAccountByIds(Long[] ids);

    /**
     * 删除第三方账户绑定信息
     *
     * @param id 第三方账户绑定主键
     * @return 结果
     */
    public int deleteSysUserThirdAccountById(Long id);
}

2.3.5:service实现类

package com.ruoyi.system.service.impl;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.system.domain.SysUserThirdAccount;
import com.ruoyi.system.mapper.SysUserThirdAccountMapper;
import com.ruoyi.system.service.ISysUserThirdAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author xiao_he
 */
@Service("sysUserThirdAccountService")
public class SysUserThirdAccountServiceImpl implements ISysUserThirdAccountService {
    @Autowired
    private SysUserThirdAccountMapper sysUserThirdAccountMapper;

    /**
     * 查询第三方账户绑定
     *
     * @param id 第三方账户绑定主键
     * @return 第三方账户绑定
     */
    @Override
    public SysUserThirdAccount selectSysUserThirdAccountById(Long id) {
        return sysUserThirdAccountMapper.selectSysUserThirdAccountById(id);
    }

    /**
     * 查询第三方账户绑定列表
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 第三方账户绑定
     */
    @Override
    public List selectSysUserThirdAccountList(SysUserThirdAccount sysUserThirdAccount) {
        return sysUserThirdAccountMapper.selectSysUserThirdAccountList(sysUserThirdAccount);
    }

    /**
     * 新增第三方账户绑定
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 结果
     */
    @Override
    public int insertSysUserThirdAccount(SysUserThirdAccount sysUserThirdAccount) {
        sysUserThirdAccount.setCreateTime(DateUtils.getNowDate());
        return sysUserThirdAccountMapper.insertSysUserThirdAccount(sysUserThirdAccount);
    }

    /**
     * 修改第三方账户绑定
     *
     * @param sysUserThirdAccount 第三方账户绑定
     * @return 结果
     */
    @Override
    public int updateSysUserThirdAccount(SysUserThirdAccount sysUserThirdAccount) {
        sysUserThirdAccount.setUpdateTime(DateUtils.getNowDate());
        return sysUserThirdAccountMapper.updateSysUserThirdAccount(sysUserThirdAccount);
    }

    /**
     * 批量删除第三方账户绑定
     *
     * @param ids 需要删除的第三方账户绑定主键
     * @return 结果
     */
    @Override
    public int deleteSysUserThirdAccountByIds(Long[] ids) {
        return sysUserThirdAccountMapper.deleteSysUserThirdAccountByIds(ids);
    }

    /**
     * 删除第三方账户绑定信息
     *
     * @param id 第三方账户绑定主键
     * @return 结果
     */
    @Override
    public int deleteSysUserThirdAccountById(Long id) {
        return sysUserThirdAccountMapper.deleteSysUserThirdAccountById(id);
    }
}

2.3.5:SysUserThirdAccountController

package cn.hfm.web.controller.system;

import java.util.List;
import javax.servlet.http.HttpServletResponse;

import cn.hfm.common.utils.SecurityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.hfm.common.annotation.Log;
import cn.hfm.common.core.controller.BaseController;
import cn.hfm.common.core.domain.AjaxResult;
import cn.hfm.common.enums.BusinessType;
import cn.hfm.system.domain.SysUserThirdAccount;
import cn.hfm.system.service.ISysUserThirdAccountService;
import cn.hfm.common.utils.poi.ExcelUtil;
import cn.hfm.common.core.page.TableDataInfo;

/**
 * 第三方账户绑定Controller
 *
 * @author xiao_he
 * @date 2023-07-07
 */
@RestController
@RequestMapping("/system/account")
public class SysUserThirdAccountController extends BaseController {
    @Autowired
    private ISysUserThirdAccountService sysUserThirdAccountService;

    /**
     * 查询第三方账户绑定列表
     */
    @PreAuthorize("@ss.hasPermi('system:account:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysUserThirdAccount sysUserThirdAccount) {
        startPage();
        List list = sysUserThirdAccountService.selectSysUserThirdAccountList(sysUserThirdAccount);
        return getDataTable(list);
    }

    /**
     * 导出第三方账户绑定列表
     */
    @PreAuthorize("@ss.hasPermi('system:account:export')")
    @Log(title = "第三方账户绑定", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysUserThirdAccount sysUserThirdAccount) {
        List list = sysUserThirdAccountService.selectSysUserThirdAccountList(sysUserThirdAccount);
        ExcelUtil util = new ExcelUtil(SysUserThirdAccount.class);
        util.exportExcel(response, list, "第三方账户绑定数据");
    }

    /**
     * 获取第三方账户绑定详细信息
     */
    @PreAuthorize("@ss.hasPermi('system:account:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return AjaxResult.success(sysUserThirdAccountService.selectSysUserThirdAccountById(id));
    }

    /**
     * 新增第三方账户绑定
     */
    @PreAuthorize("@ss.hasPermi('system:account:add')")
    @Log(title = "第三方账户绑定", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SysUserThirdAccount sysUserThirdAccount) {
        return toAjax(sysUserThirdAccountService.insertSysUserThirdAccount(sysUserThirdAccount));
    }

    /**
     * 修改第三方账户绑定
     */
    @PreAuthorize("@ss.hasPermi('system:account:edit')")
    @Log(title = "第三方账户绑定", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysUserThirdAccount sysUserThirdAccount) {
        return toAjax(sysUserThirdAccountService.updateSysUserThirdAccount(sysUserThirdAccount));
    }

    /**
     * 删除第三方账户绑定
     */
    @PreAuthorize("@ss.hasPermi('system:account:remove')")
    @Log(title = "第三方账户绑定", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(sysUserThirdAccountService.deleteSysUserThirdAccountByIds(ids));
    }


    /**
     * 查询当前用户第三方账户绑定列表
     */
    @GetMapping("/thirdAccountList")
    public TableDataInfo thirdAccountList(SysUserThirdAccount sysUserThirdAccount) {
        //设置优化id
        sysUserThirdAccount.setUserId(SecurityUtils.getUserId());
        sysUserThirdAccount.setBindFlag("1");
        startPage();
        List list = sysUserThirdAccountService.selectSysUserThirdAccountList(sysUserThirdAccount);
        return getDataTable(list);
    }
}

第三步:若依的登录鉴权使用了springSecurity,现在我们对它进行基本的配置

3.1:创建OtherAuthenticationToken继承AbstractAuthenticationToken,里面的代码照抄就行了

package com.ruoyi.framework.security.othreLogin;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * @author xiao_he
 */
public class OtherAuthenticationToken extends AbstractAuthenticationToken {
    private final Object principal;

    public OtherAuthenticationToken(Object principal) {
        super(null);
        this.principal = principal;
        this.setAuthenticated(false);
    }

    public OtherAuthenticationToken(Object principal, Collection 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 setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        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();
    }
}

3.2:创建OtherAuthenticationProvider实现AuthenticationProvider,这里的UserDetailsService在后面定义

package com.ruoyi.framework.security.othreLogin;

import com.ruoyi.framework.web.service.UserDetailsByOtherLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.Collections;

/**
 * @author xiao_he
 */
@Component
public class OtherAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    @Qualifier("userDetailsByOtherLoginService")
    private UserDetailsService userDetailsByOtherLoginService;

    /**
     * 认证逻辑
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        OtherAuthenticationToken otherAuthenticationToken = (OtherAuthenticationToken) authentication;
        String thirdUniqueAccountId = (String) otherAuthenticationToken.getPrincipal();
        UserDetails user = userDetailsByOtherLoginService.loadUserByUsername(thirdUniqueAccountId);
        OtherAuthenticationToken result = new OtherAuthenticationToken(user, Collections.emptyList());
        /*
        Details 中包含了 ip地址、 sessionId 等等属性 也可以存储一些自己想要放进去的内容
        */
        result.setDetails(otherAuthenticationToken.getDetails());
        return result;
    }

    /**
     * UserIdAuthenticationToken交给UserIdAuthenticationProvider处理
     *
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(Class aClass) {
        return OtherAuthenticationToken.class.isAssignableFrom(aClass);
    }
}

3.3:创建一个工具类,用来获取第三方token、获取用户信息

package com.ruoyi.framework.security.othreLogin;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.domain.SysUserThirdAccount;
import com.ruoyi.system.service.ISysUserThirdAccountService;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.IOException;
import java.util.Date;
import java.util.List;


/**
 * @author xiao_he
 */
public class UserDetailsUtils {
    private static final Logger log = LoggerFactory.getLogger(UserDetailsUtils.class);

    public static UserDetails createLoginUser(SysUser user) {
        SysPermissionService permissionService = SpringUtils.getBean("sysPermissionService");
        if (StringUtils.isNull(user)) {
            log.info("该用户未绑定账户,请先进行注册后绑定!");
            throw new ServiceException("该用户未绑定账户,请先进行注册后绑定!");
        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
            log.info("登录用户:{} 已被删除.", user.getUserName());
            throw new ServiceException("对不起,您的账号:" + user.getUserName() + " 已被删除");
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户:{} 已被停用.", user.getUserName());
            throw new ServiceException("对不起,您的账号:" + user.getUserName() + " 已停用");
        }
        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
    }

    public static void bindOtherUser(String token, String loginType, SysUser sysUser) throws IOException {
        if (StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(loginType)) {
            JSONObject jsonObject = getUserInfo(token, loginType);
            Object otherId = jsonObject.get("id");
            SysUserThirdAccount sysUserThirdAccount = new SysUserThirdAccount();
            sysUserThirdAccount.setBindType(loginType);
            sysUserThirdAccount.setThirdUniqueName(jsonObject.getString("name"));
            sysUserThirdAccount.setThirdUniqueAvatar(jsonObject.getString("avatar_url"));
            sysUserThirdAccount.setDelFlag("0");
            sysUserThirdAccount.setBindFlag("1");
            sysUserThirdAccount.setBindDate(new Date());
            sysUserThirdAccount.setThirdUniqueAccount(otherId.toString());
            ISysUserThirdAccountService userThirdAccountService = SpringUtils.getBean("sysUserThirdAccountService");
            List sysUserThirdAccounts = userThirdAccountService.selectSysUserThirdAccountList(sysUserThirdAccount);
            if (sysUserThirdAccounts.size() < 1) {
                sysUserThirdAccount.setUserId(sysUser.getUserId());
                userThirdAccountService.insertSysUserThirdAccount(sysUserThirdAccount);
            }
        }

    }
    /**
     * 获取Access Token
     *
     * @param url
     * @param loginType 用来拓展以后还有其他的登录逻辑处理
     * @return
     * @throws IOException
     */
    public static JSONObject getAccessToken(String url, String loginType) {
        HttpClient client = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
        HttpResponse response = null;
        try {
            response = client.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (null != entity) {
                String result = EntityUtils.toString(entity, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            httpPost.releaseConnection();
        }
        return null;
    }

    /**
     * 获取用户信息
     *
     * @param tokenId
     * @param loginType 用来拓展以后还有其他的登录逻辑处理,
     * @return
     */
    public static JSONObject getUserInfo(String tokenId, String loginType) {
        String url = "https://gitee.com/api/v5/user?access_token=" + tokenId;
        CloseableHttpClient client = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
        HttpResponse response = null;
        try {
            response = client.execute(httpGet);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            httpGet.releaseConnection();
        }
        return null;
    }
}

3.4:Constants常量类中添加几个常量


/**
 * REDIRECT 重定向地址
 */
public static final String REDIRECT = "redirect";

/**
 * AUTH_METHOD 授权类型
 */
public static final String AUTH_METHOD = "authMethod";
/**
 * AUTH_METHOD_KEY 登录方式 redisKey
 */
public static final String AUTH_METHOD_KEY = "authMethod:";


/**
 * LOGIN_TYPE 登录方式
 */
public static final String LOGIN_TYPE = "loginType";


public static final String OTHER_ID = "otherId";

3.5:LoginBody类中添加字段,并提供get/set方法

/**
 * 第三方token
 */
private String token;

/**
 * 登录类型
 */
private String loginType;

3.6:创建UserDetailsByOtherLoginService实现UserDetailsService做第三方登录处理逻辑

package com.ruoyi.framework.web.service;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.security.othreLogin.UserDetailsUtils;
import com.ruoyi.system.domain.SysUserThirdAccount;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.ISysUserThirdAccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;


/**
 * @author xiao_he
 */
@Service("userDetailsByOtherLoginService")
public class UserDetailsByOtherLoginService implements UserDetailsService {
    @Autowired
    private ISysUserService userService;


    @Autowired
    private ISysUserThirdAccountService userThirdAccountService;

    @Override
    public UserDetails loadUserByUsername(String tokenId) throws UsernameNotFoundException {
        JSONObject jsonObject = null;
        //在session里拿loginType
        String loginType = (String) ServletUtils.getRequest().getSession().getAttribute(Constants.LOGIN_TYPE);
        jsonObject = UserDetailsUtils.getUserInfo(tokenId, loginType);
        Object thirdUniqueAccountId = jsonObject.get("id");
        SysUserThirdAccount sysUserThirdAccount = new SysUserThirdAccount();
        sysUserThirdAccount.setBindType(loginType);
        sysUserThirdAccount.setDelFlag("0");
        sysUserThirdAccount.setBindFlag("1");
        sysUserThirdAccount.setThirdUniqueAccount(thirdUniqueAccountId.toString());
        List sysUserThirdAccounts = userThirdAccountService.selectSysUserThirdAccountList(sysUserThirdAccount);
        Long userId = 0L;
        if (sysUserThirdAccounts.size() > 0) {
            userId = sysUserThirdAccounts.get(0).getUserId();
        } else {
            SysUser user = new SysUser();
            user.setUserId(-1L);
            ServletUtils.getRequest().setAttribute(Constants.OTHER_ID, thirdUniqueAccountId);
            //没有绑定用户,伪造一个用户,防止报错
            return new LoginUser(-1L, null, user, null);
        }
        SysUser user = userService.selectUserById(userId);
        UserDetails loginUser = UserDetailsUtils.createLoginUser(user);
        return loginUser;
    }
}

3.7:在原来UserDetailsServiceImpl上添加注解,区分是哪个处理逻辑

@Service("userDetailsByPasswordService")

3.8:配置SecurityConfig

3.8.1:在原来的注入UserDetailsService类上添加注解指定userDetailsByPasswordService

/**
 * 自定义用户认证逻辑
 */
@Autowired
@Qualifier("userDetailsByPasswordService")
private UserDetailsService userDetailsService;

3.8.2:添加认证白名单,这几个链接配置为免登录链接

.antMatchers("/otherLogin", "/preOtherAuthUrl", "/otherLoginCallback/**","/system/dict/data/type/**").permitAll()

3.8.3:将AuthenticationGiteeProvider注入进来,配置configure方法,添加一行auth.authenticationProvider(authenticationGiteeProvider);

@Autowired
private AuthenticationGiteeProvider authenticationGiteeProvider;

/**
 * 身份认证接口
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    auth.authenticationProvider(authenticationGiteeProvider);
}

3.8.4:SysLoginService中添加登录方法和绑定方法

/**
 * 登录验证
 *
 * @param token     登录验证token
 * @param loginType 登录类型,后期拓展
 * @return 结果
 */
public String otherLogin(String token, String loginType) {
    // 用户验证
    Authentication authentication = null;
    try {
        // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
        if ("1".equals(loginType) || "2".equals(loginType) || "3".equals(loginType)) {
            authentication = authenticationManager.authenticate(new OtherAuthenticationToken(token));
        }
    } catch (Exception e) {
        if (e instanceof BadCredentialsException) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor("username", Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        } else {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor("username", Constants.LOGIN_FAIL, e.getMessage()));
            throw new ServiceException(e.getMessage());
        }
    }
    AsyncManager.me().execute(AsyncFactory.recordLogininfor(authentication.getName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
    LoginUser loginUser = (LoginUser) authentication.getPrincipal();
    recordLoginInfo(loginUser.getUserId());
    // 生成token
    return tokenService.createToken(loginUser);
}

/**
 * 绑定第三方账户
 *
 * @param tokenId   第三放认证后的token
 * @param loginType 登录类型
 */
public void bindThirdAccount(String tokenId, String loginType) {
    LoginUser loginUser = SecurityUtils.getLoginUser();
    SysUser sysUser = loginUser.getUser();
    JSONObject jsonObject = null;
    jsonObject = UserDetailsUtils.getUserInfo(tokenId, loginType);
    SysUserThirdAccount thirdAccount = new SysUserThirdAccount();
    thirdAccount.setBindType(loginType);
    thirdAccount.setDelFlag("0");
    thirdAccount.setBindFlag("1");
    thirdAccount.setThirdUniqueAccount(jsonObject.getString("id"));
    List sysUserThirdAccounts = userThirdAccountService.selectSysUserThirdAccountList(thirdAccount);
    if (sysUserThirdAccounts.size() > 0) {
        //绑定过账户
        SysUserThirdAccount userThirdAccount = sysUserThirdAccounts.get(0);
        if (!userThirdAccount.equals(SecurityUtils.getUserId())) {
            throw new ServiceException("该合作账户已经绑定过其他账户,请先解绑其他账户!");
        }
        userThirdAccount.setThirdUniqueName(jsonObject.getString("name"));
        userThirdAccount.setThirdUniqueAvatar(jsonObject.getString("avatar_url"));
        userThirdAccount.setBindFlag("1");
        userThirdAccount.setBindDate(new Date());
        userThirdAccount.setUserId(SecurityUtils.getUserId());
        userThirdAccountService.updateSysUserThirdAccount(userThirdAccount);
    } else {
        //看看当前账户有没有已经解绑的同类型,并且同tokenId账户,有解绑的了的,覆盖掉
        thirdAccount.setBindFlag("2");
        sysUserThirdAccounts = userThirdAccountService.selectSysUserThirdAccountList(thirdAccount);
        //设置绑定时间,以及第三方账号id
        thirdAccount.setThirdUniqueName(jsonObject.getString("name"));
        thirdAccount.setThirdUniqueAvatar(jsonObject.getString("avatar_url"));
        thirdAccount.setBindDate(new Date());
        thirdAccount.setBindFlag("1");
        thirdAccount.setUserId(SecurityUtils.getUserId());
        thirdAccount.setThirdUniqueAccount(jsonObject.getString("id"));
        if (sysUserThirdAccounts.size() > 0) {
            thirdAccount.setId(sysUserThirdAccounts.get(0).getId());
            userThirdAccountService.updateSysUserThirdAccount(thirdAccount);
        } else {
            userThirdAccountService.insertSysUserThirdAccount(thirdAccount);
        }
    }
}

3.8.5:创建OtherLoginController

package com.ruoyi.web.controller.system;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.framework.security.othreLogin.UserDetailsUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.domain.SysUserThirdAccount;
import com.ruoyi.system.service.ISysUserThirdAccountService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * @author xiao_he
 */
@RestController
public class OtherLoginController {
    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ISysUserThirdAccountService userThirdAccountService;

    @Autowired
    private RedisCache redisCache;

    /**
     * gitee授权中提供的 appid 和 appkey
     */
    @Value("${otherLogin.gitee.clientid}")
    public String  GIT_EE_CLIENT_ID;
    @Value("${otherLogin.gitee.clientsecret}")
    public String GIT_EE_CLIENT_SECRET;
    @Value("${otherLogin.gitee.callback}")
    public String GIT_EE_CLIENT_URL;
    @Value("${ruoyi.doMain}")
    public String DO_MAIN;


    /**
     * 其他合作账户登录的公共方法
     *
     * @param loginBody
     * @param request
     * @return
     */
    @PostMapping("/otherLogin")
    public AjaxResult otherLogin(@RequestBody LoginBody loginBody, HttpServletRequest request) {
        AjaxResult ajax = AjaxResult.success();
        String redirect = String.valueOf(request.getSession().getAttribute(Constants.REDIRECT));
        //授权类型
        String method = (String) redisCache.getCacheObject(Constants.AUTH_METHOD_KEY + loginBody.getUuid());
        request.getSession().setAttribute(Constants.LOGIN_TYPE, loginBody.getLoginType());
        if ("login".equals(method)) {
            //验证token
            String token = loginService.otherLogin(loginBody.getToken(), loginBody.getLoginType());
            Object otherID = ServletUtils.getRequest().getAttribute(Constants.OTHER_ID);
            if (ObjectUtils.isEmpty(otherID)) {
                ajax.put(Constants.OTHER_ID, null);
                ajax.put(Constants.TOKEN, token);
                ajax.put(Constants.AUTH_METHOD, method);
                if (StringUtils.isNotEmpty(redirect) && (!"undefined".equals(redirect)) && (!"null".equals(redirect))) {
                    ajax.put(Constants.REDIRECT, redirect);
                } else {
                    ajax.put(Constants.REDIRECT, null);
                }
            } else {
                //么有token
                ajax.put(Constants.TOKEN, null);
                ajax.put(Constants.OTHER_ID, otherID);
            }
        } else {
            //这是绑定账户走的
            loginService.bindThirdAccount(loginBody.getToken(), loginBody.getLoginType());
            ajax.put(Constants.AUTH_METHOD, method);
        }
        return ajax;
    }


    /**
     * 解绑第三方账户
     */
    @Log(title = "解绑第三方账户", businessType = BusinessType.UPDATE)
    @PostMapping("/unBindThirdAccount")
    public AjaxResult unBindThirdAccount(@RequestBody SysUserThirdAccount userThirdAccount) throws IOException {
        userThirdAccount.setUserId(SecurityUtils.getUserId());
        //查询当前账号的绑定账号
        userThirdAccount.setBindFlag("1");
        List sysUserThirdAccounts = userThirdAccountService.selectSysUserThirdAccountList(userThirdAccount);
        if (sysUserThirdAccounts.size() > 0) {
            SysUserThirdAccount sysUserThirdAccount = sysUserThirdAccounts.get(0);
            //解绑账号
            sysUserThirdAccount.setBindFlag("2");
            userThirdAccountService.updateSysUserThirdAccount(sysUserThirdAccount);
            return AjaxResult.success();
        }
        return AjaxResult.error("未找到绑定账号!");
    }


    /**
     * 获取第三方登录认证url
     *
     * @param request
     * @return
     */
    @GetMapping("/preOtherAuthUrl")
    public AjaxResult preOtherAuthUrl(HttpServletRequest request) {
        AjaxResult ajax = AjaxResult.success();
        HttpSession session = request.getSession();
        // 用于第三方应用防止CSRF攻击
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        session.setAttribute("state", uuid);
        String redirect = request.getParameter(Constants.REDIRECT);
        if (StringUtils.isNotEmpty(redirect)) {
            session.setAttribute(Constants.REDIRECT, redirect);
        }
        //授权类型
        redisCache.setCacheObject(Constants.AUTH_METHOD_KEY + uuid, request.getParameter(Constants.AUTH_METHOD), Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        // Step1:获取Authorization Code
        String authorizeUrl = "https://gitee.com/oauth/authorize?response_type=code" +
                "&client_id=" + GIT_EE_CLIENT_ID +
                "&redirect_uri=" + GIT_EE_CLIENT_URL +
                "&state=" + uuid +
                "&scope=user_info";
        //存储
        ajax.put("authorizeUrl", authorizeUrl);
        ajax.put("uuid", uuid);
        return ajax;
    }


    /**
     *  授权后的回调方法
     * @param loginType 第三方登录类型
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @GetMapping("/otherLoginCallback/{loginType}")
    public AjaxResult otherLoginCallback(@PathVariable("loginType") String loginType, HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        // 得到Authorization Code
        String code = request.getParameter("code");
        // 我们放在地址中的状态码
        String state = request.getParameter("state");
        String uuid = (String) session.getAttribute("state");
        //类型
        String method = (String) redisCache.getCacheObject(Constants.AUTH_METHOD_KEY + uuid);
        // 验证信息我们发送的状态码
        if (null != uuid) {
            // 状态码不正确,直接返回登录页面
            if (!uuid.equals(state)) {
                return AjaxResult.error();
            }
        }
        // Step1:获取Authorization Code
        String authorizeUrl = "https://gitee.com/oauth/token?grant_type=authorization_code" +
                "&client_id=" + GIT_EE_CLIENT_ID +
                "&client_secret=" + GIT_EE_CLIENT_SECRET +
                "&code=" + code +
                "&redirect_uri=" + GIT_EE_CLIENT_URL;
        // Step2:通过Authorization Code获取Access Token
        String redirect = "";
        JSONObject accessTokenJson = UserDetailsUtils.getAccessToken(authorizeUrl,loginType);
        Object redirectPath = session.getAttribute(Constants.REDIRECT);
        if ("login".equals(method)) {
            if (redirectPath != null) {
                redirect = DO_MAIN + "/cmsLogin?redirect=" + redirectPath + "&callback_token=" + accessTokenJson.get("access_token") + "&callback_login_type=" + loginType + "&callback_uuid=" + uuid;
            } else {
                redirect = DO_MAIN + "/cmsLogin" + "?callback_token=" + accessTokenJson.get("access_token") + "&callback_login_type=" + loginType + "&callback_uuid=" + uuid;
            }
        } else {
            redirect = DO_MAIN + "/user/profile?callback_token=" + accessTokenJson.get("access_token") + "&callback_login_type=" + loginType + "&callback_uuid=" + uuid;
        }
        response.sendRedirect(redirect);
        return AjaxResult.success();
    }
}

第四步:开始编写前端代码

4.1:在login.js添加方法

//第三方登录获取url
export function preOtherAuthUrl(redirect, authMethod, loginType) {
  return request({
    url: '/preOtherAuthUrl',
    headers: {
      isToken: false
    },
    params: {
      'redirect': redirect,
      'authMethod': authMethod,
      'loginType': loginType
    },
    method: 'get',
  })
}

export function otherLogin(token, loginType, uuid) {
  const data = {
    token,
    loginType,
    uuid
  }
  return request({
    url: '/otherLogin',
    method: 'post',
    data: data
  })
}

// 修改用户个人信息
export function unBindThirdAccount(data) {
  return request({
    url: '/unBindThirdAccount',
    method: 'post',
    data: data
  })
}

4.2:ruoyi-ui/src/store/modules/user.js添加方法

otherLogin({commit}, body) {
  return new Promise((resolve, reject) => {
    otherLogin(body.token, body.loginType, body.uuid).then(res => {
      let token = res.token;
      if ('login' == res.authMethod && null != token) {
        //如果是登录请求,需要重新设置token
        setToken(res.token)
        commit('SET_TOKEN', res.token)
      }
      resolve(res)
    }).catch(error => {
      reject(error)
    })
  })
},

3.8.6:需要加入的字典数据,这里后面会用到

INSERT INTO `sys_dict_type` VALUES (104,'第三方用户状态','user_third_status','0','plat','2023-07-07 11:23:04','',NULL,NULL);
INSERT INTO `sys_dict_data` VALUES (108,0,'已绑定','1','user_third_status',NULL,'success','N','0','plat','2023-07-07 11:24:00','',NULL,NULL);
INSERT INTO `sys_dict_data` VALUES (109,1,'未绑定','0','user_third_status',NULL,'danger','N','0','plat','2023-07-07 11:24:37','plat','2023-07-07 11:25:08',NULL);
INSERT INTO `sys_dict_data` VALUES (110,2,'已解绑','2','user_third_status',NULL,'danger','N','0','plat','2023-07-07 11:24:57','',NULL,NULL);

INSERT INTO `sys_dict_type` VALUES (103,'合作平台','third_account_type','0','plat','2023-07-07 11:23:04','plat','2023-10-13 01:27:12','第三方数据登录平台');
INSERT INTO `sys_dict_data` VALUES (106,0,'gitee','1','third_account_type',NULL,'','N','0','plat','2023-07-07 11:24:00','',NULL,NULL);

4.3:前端模块代码

4.3.1:添加一个gitee的图标,在项目路径src/assets/icons/svg/下新建gitee.svg文件,内容把这些复制进去


  
    
    
  

4.3.2:添加字典,写在script下,这个是用来获取字典数据的

dicts: ['third_account_type'],

若依实现第三方登录,史上最全保姆级教程_第3张图片

4.3.3:添加按钮


  其他方式登录
  

4.3.4:添加样式,这个随意,你们可以自己调整

.loginMethods {
  display: flex;
  justify-content: center;
  align-content: center;
  gap: 20px;

  .loginMethod {
    cursor: pointer;
    font-size: 35px;
  }
}

4.3.5:添加登录方法

otherLogin(item) {
  let that = this
  this.$modal.loading('正在登录中,请稍后')
  preOtherAuthUrl(that.redirect, 'login', item).then(res => {
    window.location = res.authorizeUrl
  })
},

4.3.6:添加生命周期函数

mounted() {
  let token = this.$route.query.callback_token
  let loginType = this.$route.query.callback_login_type
  let uuid = this.$route.query.callback_uuid
  if (token == 'null') {
    this.$modal.msgWarning('用户取消授权')
  } else if (token != undefined && token != null && token != '' && token != 'null' && loginType != undefined && loginType != null && loginType != '') {
    const formBody = {
      token: token,
      loginType: loginType,
      uuid: uuid
    }
    this.$modal.loading('正在登录中,请稍后')
    this.$store.dispatch('otherLogin', formBody).then((res) => {
        this.$router.push({path: res.redirect || '/cms/main/cmsIndex'}).catch(() => {
        })
    }).catch((res) => {
    }).finally(res => {
      this.$modal.closeLoading()
    })
  }
}

第五步:绑定页面设计

若依实现第三方登录,史上最全保姆级教程_第4张图片

5.1:第三方绑定组件,我把这个单独抽出了一个组件处理






5.2:在src/views/system/user/profile/index.vue里面引用该组件


  

本文到这里就结束了,感谢您的阅读,可以点击这里去我的博客看看哦

你可能感兴趣的:(java)