认证服务------功能实现逻辑2

简介: 认证服务------功能实现逻辑2

3>密码进行MD5盐值加密

4>保存用户信息到数据库中

package com.saodai.saodaimall.member.vo;
import lombok.Data;
/**
 * 会员注册类
 **/
@Data
public class MemberUserRegisterVo {
    private String userName;
    private String password;
    private String phone;
}
package com.saodai.saodaimall.member.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * 会员
 */
@Data
@TableName("ums_member")
public class MemberEntity implements Serializable {
  private static final long serialVersionUID = 1L;
  /**
   * id
   */
  @TableId
  private Long id;
  /**
   * 会员等级id
   */
  private Long levelId;
  /**
   * 用户名
   */
  private String username;
  /**
   * 密码
   */
  private String password;
  /**
   * 昵称
   */
  private String nickname;
  /**
   * 手机号码
   */
  private String mobile;
  /**
   * 邮箱
   */
  private String email;
  /**
   * 头像
   */
  private String header;
  /**
   * 性别
   */
  private Integer gender;
  /**
   * 生日
   */
  private Date birth;
  /**
   * 所在城市
   */
  private String city;
  /**
   * 职业
   */
  private String job;
  /**
   * 个性签名
   */
  private String sign;
  /**
   * 用户来源
   */
  private Integer sourceType;
  /**
   * 积分
   */
  private Integer integration;
  /**
   * 成长值
   */
  private Integer growth;
  /**
   * 启用状态
   */
  private Integer status;
  /**
   * 注册时间
   */
  private Date createTime;
  /**
   * 社交登录用户的ID
   */
  private String socialId;
  /**
   * 社交登录用户的名称
   */
  private String socialName;
  /**
   * 社交登录用户的自我介绍
   */
  private String socialBio;
}


二、用户登录功能

(1)前端以表单的形式发送请求/login给后端

<form action="/login" method="post">
  <div style="color: red;height: 10px;text-align: center;" th:text="${errors != null ? (#maps.containsKey(errors, 'msg') ? errors.msg : '') : ''}"></div>
  <ul>
    <li class="top_1">
      <img src="/static/login/JD_img/user_03.png" class="err_img1"/>
      <input type="text" name="loginacct" placeholder=" 邮箱/用户名/已验证手机" class="user"/>
    </li>
    <li>
      <img src="/static/login/JD_img/user_06.png" class="err_img2"/>
      <input type="password" name="password" placeholder=" 密码" class="password"/>
    </li>
    <li class="bri">
      <a href="/static/login/" >忘记密码</a>
    </li>
    <li class="ent">
      <button class="btn2" type="submit">登 &nbsp; &nbsp;录</a></button>
  </li>
 </ul>
</form>

(2)在认证服务的LoginController来处理请求/login

/**
* 普通手机账号登录
* @param vo
* @param attributes
* @param session
* @return
*/
@PostMapping(value = "/login")
public String login(UserLoginVo vo, RedirectAttributes attributes, HttpSession session) {
    //远程登录
    R login = memberFeignService.login(vo);
    //login.getCode() == 0表示远程调用成功
    if (login.getCode() == 0) {
        MemberResponseVo data = login.getData("data", new TypeReference<MemberResponseVo>() {});
        session.setAttribute(LOGIN_USER,data);
        return "redirect:http://saodaimall.com";
    } else {
        Map<String,String> errors = new HashMap<>();
        errors.put("msg",login.getData("msg",new TypeReference<String>(){}));
        attributes.addFlashAttribute("errors",errors);
        return "redirect:http://auth.saodaimall.com/login.html";
    }
    }
package com.saodai.saodaimall.auth.vo;
import lombok.Data;
/**
 * 用户登录信息封装类
 **/
@Data
public class UserLoginVo {
    //账户
    private String loginacct;
    //密码
    private String password;
}
package com.saodai.common.vo;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
import java.util.Date;
/**
 *会员信息
 **/
@ToString
@Data
public class MemberResponseVo implements Serializable {
    private static final long serialVersionUID = 5573669251256409786L;
    private Long id;
    /**
     * 会员等级id
     */
    private Long levelId;
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 昵称
     */
    private String nickname;
    /**
     * 手机号码
     */
    private String mobile;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 头像
     */
    private String header;
    /**
     * 性别
     */
    private Integer gender;
    /**
     * 生日
     */
    private Date birth;
    /**
     * 所在城市
     */
    private String city;
    /**
     * 职业
     */
    private String job;
    /**
     * 个性签名
     */
    private String sign;
    /**
     * 用户来源
     */
    private Integer sourceType;
    /**
     * 积分
     */
    private Integer integration;
    /**
     * 成长值
     */
    private Integer growth;
    /**
     * 启用状态
     */
    private Integer status;
    /**
     * 注册时间
     */
    private Date createTime;
    /**
     * 社交登录用户的ID
     */
    private String socialId;
    /**
     * 社交登录用户的名称
     */
    private String socialName;
    /**
     * 社交登录用户的自我介绍
     */
    private String socialBio;
}

(3)远程调用会员服务来实现真正的登录

 /**
     * 会员登录功能
     * @param vo
     * @return
     */
   @PostMapping(value = "/login")
    public R login(@RequestBody MemberUserLoginVo vo) {
       //MemberUserLoginVo就是上面的UserLoginVo只是类名不一样
        MemberEntity memberEntity = memberService.login(vo);
        if (memberEntity != null) {
            return R.ok().setData(memberEntity);
        } else {
            return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(),BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMessage());
        }
    }
    /**
     * 会员登录功能
     * @param vo
     * @return
     */
    @Override
    public MemberEntity login(MemberUserLoginVo vo) {
         //获取用户登录账号
        String loginacct = vo.getLoginacct();
        String password = vo.getPassword();
        //1、去数据库查询 SELECT * FROM ums_member WHERE username = ? OR mobile = ?
        //通过用户名或手机号登录都是可以的
        MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>()
                .eq("username", loginacct).or().eq("mobile", loginacct));
        if (memberEntity == null) {
            //登录失败
            return null;
        } else {
            //获取到数据库里的password
            String password1 = memberEntity.getPassword();
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            //进行密码匹配
            boolean matches = passwordEncoder.matches(password, password1);
            if (matches) {
                //登录成功
                return memberEntity;
            }
        }
        return null;
    }
package com.saodai.saodaimall.member.vo;
import lombok.Data;
/**
 * 会员登录
 **/
@Data
public class MemberUserLoginVo {
    private String loginacct;
    private String password;
}

社交登录(OAuth2.0)

这里由于微博开发者权限申请太慢了就使用gitee来实现社交登录

(1)前端调用第三方应用作为社交登录

这个跟以往的模式不一样,以往是前段直接给后端发送请求,然后后端处理请求,这个是先调用第三方应用作为社交登录(也就是先跳转到gitee的登录授权页面),然后用户登录自己的gitee账号密码进行授权,授权成功后会跳转到指定的应用回调地址,然后在后端来处理这个应用回调地址的请求

gitee开发者后台管理链接:https://gitee.com/oauth/applications/16285

调用gitee第三方登录的url地址:<a href="https://gitee.com/oauth/authorize?client_id=自己应用的Client ID&redirect_uri=自己应用的成功回调地址&response_type=code&state=1">

<li>
  <a href="https://gitee.com/oauth/authorize?client_id=32459f971ce6d89cfb9f70899525455d0653cb804f16b38a304e3447dc97d673&redirect_uri=http://auth.saodaimall.com/callback&response_type=code&state=1">
    <img style="width: 50px;height: 18px;margin-top: 35px;" src="/static/login/JD_img/gitee.png"/>
  </a>
</li>

(2)社交服务OAuth2Controller来处理应用回调地址/callback请求

分流程:(其实就只有三行代码是要自己配置的(OAuth2Controller的gitee的42-44行),其他的基本上是固定的)

1>封装AccessTokenDTO对象然后发给码云服务器,如果AccessTokenDTO对象正确的话就返回一个通行令牌(其实准确来说是用户授权后会返回一个code,然后通过code来去找码云服务器获取到一个通行令牌,最后通过这个通行令牌去找码云服务器要这个用户在gitee上公开的资料信息)


2>获取到了access_token通行令牌,转为通用gitee社交登录GiteeUser对象


3>远程调用会员服务来进行社交登录

package com.saodai.saodaimall.auth.controller;
import com.alibaba.fastjson.TypeReference;
import com.saodai.common.utils.R;
import com.saodai.common.vo.MemberResponseVo;
import com.saodai.saodaimall.auth.component.GitheeProvider;
import com.saodai.saodaimall.auth.feign.MemberFeignService;
import com.saodai.saodaimall.auth.vo.AccessTokenDTO;
import com.saodai.saodaimall.auth.vo.GiteeUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpSession;
import static com.saodai.common.constant.AuthServerConstant.LOGIN_USER;
/**
* 社交第三方授权登录
**/
@Slf4j
@Controller
public class OAuth2Controller {
    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private AccessTokenDTO accessTokenDTO;
    @Autowired
    private GitheeProvider githeeProvider;
    @GetMapping(value = "/callback")
    // /callback?code=e867a1f4575d4a6161e3249423a0403898253bc593e4b031a8771739ee6769f5&state=1
    public String gitee(@RequestParam(name = "code") String code,@RequestParam(name = "state") String state, HttpSession session) throws Exception {
        System.out.println(code);
        //下面三行代码都是自己应用的值,可以在gitee的第三方应用中看到对应的值
        accessTokenDTO.setClient_id("32459f971ce6d89cfb9f70899525455d0653cb804f16b38a304e3447dc97d673");
        accessTokenDTO.setClient_secret("f3046c911c03cadcded986062708150d4232af3ca6aef0259e5a0198d2c15ba5");
        accessTokenDTO.setRedirect_uri("http://auth.saodaimall.com/callback");
        accessTokenDTO.setCode(code);
        accessTokenDTO.setState(state);
        String accessToken = githeeProvider.getAccessToken(accessTokenDTO);
        //2、处理
        if (!StringUtils.isEmpty(accessToken)) {
            //获取到了access_token,转为通用gitee社交登录对象
            GiteeUser giteeUser = githeeProvider.getGiteeUser(accessToken);
            //知道了哪个社交用户
            //1)、当前用户如果是第一次进网站,自动注册进来(为当前社交用户生成一个会员信息,以后这个社交账号就对应指定的会员)
            //登录或者注册这个社交用户
            //调用远程服务
            R oauthLogin = memberFeignService.oauthLogin(giteeUser);
            if (oauthLogin.getCode() == 0) {
                MemberResponseVo memberResponseVo = oauthLogin.getData("data", new TypeReference<MemberResponseVo>() {});
                log.info("登录成功:用户信息:{}",memberResponseVo.toString());
                //1、第一次使用session,命令浏览器保存卡号,JSESSIONID这个cookie
                //以后浏览器访问哪个网站就会带上这个网站的cookie
                //TODO 1、默认发的令牌。当前域(解决子域session共享问题)
                //TODO 2、使用JSON的序列化方式来序列化对象到Redis中
                session.setAttribute(LOGIN_USER,memberResponseVo);
                //2、登录成功跳回首页
                return "redirect:http://saodaimall.com";
            } else {
                return "redirect:http://auth.saodaimall.com/login.html";
            }
        } else {
            return "redirect:http://auth.saodaimall.com/login.html";
        }
    }
}
package com.saodai.saodaimall.auth.vo;
/**
 * AccessTokenDTO对象封装(gitee社交登录令牌)
 */
import org.springframework.stereotype.Component;
@Component
public class AccessTokenDTO {
    private String client_id;
    private String client_secret;
    private String code;
    private String redirect_uri;
    private String state;
    public String getClient_id() {
        return client_id;
    }
    public void setClient_id(String client_id) {
        this.client_id = client_id;
    }
    public String getClient_secret() {
        return client_secret;
    }
    public void setClient_secret(String client_secret) {
        this.client_secret = client_secret;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getRedirect_uri() {
        return redirect_uri;
    }
    public void setRedirect_uri(String redirect_uri) {
        this.redirect_uri = redirect_uri;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}
package com.saodai.saodaimall.auth.component;
import com.alibaba.fastjson.JSON;
import com.saodai.saodaimall.auth.vo.AccessTokenDTO;
import com.saodai.saodaimall.auth.vo.GiteeUser;
import okhttp3.*;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
 * 请求码云服务器
 */
@Component
public class GitheeProvider {
    //发起post请求获取AccessToken
    public String getAccessToken(AccessTokenDTO accessTokenDTO){
        MediaType mediaType= MediaType.get("application/json; charset=utf-8");
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(accessTokenDTO));
        Request request = new Request.Builder()
                .url("https://gitee.com/oauth/token?grant_type=authorization_code&code="+accessTokenDTO.getCode()+
                        "&client_id="+accessTokenDTO.getClient_id()+"&redirect_uri="+accessTokenDTO.getRedirect_uri()+
                        "&client_secret="+accessTokenDTO.getClient_secret())
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            String string = response.body().string();
            System.out.println(string);
            String str1 = string.split(":")[1];
            String str2 = str1.split("\"")[1];
            return str2;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    //发起get请求返回GitUser对象,
    public GiteeUser getGiteeUser(String token){
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://gitee.com/api/v5/user?access_token="+token)
                .build();
        try (Response response = client.newCall(request).execute()) {
            String string=response.body().string();
            GiteeUser giteeUser = JSON.parseObject(string, GiteeUser.class);
            return giteeUser;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.saodai.saodaimall.auth.vo;
import lombok.Data;
/**
 * GiteeUser对象封装(社交登录的gitee对象)
 */
@Data
public class GiteeUser {
    //gitee用户名称
    private String name;
    //gitee用户id
    private String id;
    //gitee用户自我介绍
    private String bio;
}

(3)远程调用会员服务来进行社交登录

  /**
     * 社交登录
     * @param giteeUser
     * @return
     * @throws Exception
     */
  @PostMapping(value = "/oauth2/login")
    public R oauthLogin(@RequestBody GiteeUser giteeUser) throws Exception {
        MemberEntity memberEntity = memberService.login(giteeUser);
        if (memberEntity != null) {
            return R.ok().setData(memberEntity);
        } else {
            return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(),BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMessage());
        }
    }
    /**
     * 社交登录
     * @param giteeUser
     * @return
     * @throws Exception
     */
    @Override
    public MemberEntity login(GiteeUser giteeUser) throws Exception {
        //获取gitee用户唯一id
        String giteeUserId = giteeUser.getId();
        //1、判断当前社交用户是否已经登录过系统
        MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_id", giteeUserId));
        //这个用户已经注册过
        if (memberEntity != null) {
            return memberEntity;
        } else {
            //2、没有查到当前社交用户对应的记录我们就需要注册一个
            MemberEntity register = new MemberEntity();
            //社交gitee登录的id作为会员id
            register.setId(Long.valueOf(giteeUserId));
            register.setSocialName(giteeUser.getName());
            register.setUsername(giteeUser.getName());
            register.setNickname(giteeUser.getName());
            register.setCreateTime(new Date());
            register.setSocialBio(giteeUser.getBio());
            register.setSocialId(giteeUserId);
            //把用户信息插入到数据库中
            this.baseMapper.insert(register);
            return register;
        }
    }
package com.saodai.saodaimall.member.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * 会员
 */
@Data
@TableName("ums_member")
public class MemberEntity implements Serializable {
  private static final long serialVersionUID = 1L;
  /**
   * id
   */
  @TableId
  private Long id;
  /**
   * 会员等级id
   */
  private Long levelId;
  /**
   * 用户名
   */
  private String username;
  /**
   * 密码
   */
  private String password;
  /**
   * 昵称
   */
  private String nickname;
  /**
   * 手机号码
   */
  private String mobile;
  /**
   * 邮箱
   */
  private String email;
  /**
   * 头像
   */
  private String header;
  /**
   * 性别
   */
  private Integer gender;
  /**
   * 生日
   */
  private Date birth;
  /**
   * 所在城市
   */
  private String city;
  /**
   * 职业
   */
  private String job;
  /**
   * 个性签名
   */
  private String sign;
  /**
   * 用户来源
   */
  private Integer sourceType;
  /**
   * 积分
   */
  private Integer integration;
  /**
   * 成长值
   */
  private Integer growth;
  /**
   * 启用状态
   */
  private Integer status;
  /**
   * 注册时间
   */
  private Date createTime;
  /**
   * 社交登录用户的ID
   */
  private String socialId;
  /**
   * 社交登录用户的名称
   */
  private String socialName;
  /**
   * 社交登录用户的自我介绍
   */
  private String socialBio;
}


相关实践学习
2分钟自动化部署人生模拟器
本场景将带你借助云效流水线Flow实现人生模拟器小游戏的自动化部署
7天玩转云服务器
云服务器ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,可降低 IT 成本,提升运维效率。本课程手把手带你了解ECS、掌握基本操作、动手实操快照管理、镜像管理等。了解产品详情:&nbsp;https://www.aliyun.com/product/ecs
目录
相关文章
|
云安全 缓存 NoSQL
认证服务------功能实现逻辑
认证服务------功能实现逻辑
90 0
|
4月前
|
数据采集 安全 容灾
《阿里云产品手册2022-2023 版》——号码认证服务
《阿里云产品手册2022-2023 版》——号码认证服务
|
移动开发 API 开发工具
秒懂云通信:如何使用阿里云号码认证服务(小白指南)
手把手教你如何使用阿里云号码认证服务,超详细控制台步骤解析,快速上手!
3013 0
秒懂云通信:如何使用阿里云号码认证服务(小白指南)
|
安全 数据安全/隐私保护 开发者
阿里云通信发布全新号码认证服务, 重新定义手机号码认证的方式
12月12日,阿里云通信宣布号码认证服务正式商用,将重新定义手机号码认证的方式。因移动应用实名制的政策要求,手机号码认证在移动APP的注册、登录等场景用的越来越多。而对于开发者来说,能完成手机号码认证的选择并不多,一般是借助短信、语音的基础通信通道,自己实现短信验证码或语音验证码来实现。
25062 0
|
7月前
|
NoSQL Redis 数据安全/隐私保护
若依框架----token权限控制逻辑
若依框架----token权限控制逻辑
839 0
|
7月前
|
前端开发 JavaScript 关系型数据库
若依框架------后台路由数据是如何转换为前端路由信息的
若依框架------后台路由数据是如何转换为前端路由信息的
721 0
|
1月前
|
存储 开发工具 数据库
认证源码分析与自定义后端认证逻辑
认证源码分析与自定义后端认证逻辑
34 0
认证源码分析与自定义后端认证逻辑
|
6月前
|
前端开发 数据库 索引
前后端分离------后端创建笔记(05)用户列表查询接口(下)
前后端分离------后端创建笔记(05)用户列表查询接口(下)