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">登 录</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; }