大技术
Nacos做注册中心
把新建的微服务注册到Nacos上去
两个步骤
- 在配置文件中配置应用名称、nacos的发现注册ip地址,端口号
- 在启动类上用@EnableDiscoveryClient注解开启注册功能
使用Redis存验证码信息
加入依赖配置地址和端口号即可
直接注入StringRedisTemplate模板类用就是
使用如下
//根据key获取redis中的值
String redisCode= redisTemplate.opsForValue().get(redis中的key值);
//存值到Redis中
redisTemplate.opsForValue().set(key值,value值,过期时间, 过期单位(TimeUnit.MINUTES));
使用gitee作社交登录(OAuth2.0)(亮点1)
这里由于微博开发者权限申请太慢了就使用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对象正确的话就返回一个access_token通行令牌(其实准确来说是用户授权后会返回一个code,然后把code和其他一些信息封装成AccessTokenDTO对象去找码云服务器获取到一个access_token通行令牌,最后通过这个access_token通行令牌去找码云服务器要这个用户在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; }
整合SpringSession来解决session不同步不共享的问题(亮点2)
使用SpringSession的目的是来解决分布式session不同步不共享的问题。使用SpringSession可以把session都存在redis中,这样就解决了session不同步的问题,然后扩大作用域,这就解决了session不共享的问题,SpringSession不需要显性的操作(也就是不需要用StringRedisTemplate类的方法来把session放到redis中去,啥都不用干,就正常的把数据放到HttpSession中就可),由于整合了SpringSession,所以放到HttpSession中的数据会自动的放到redis中去,由于配置了序列化,所以session会被序列化json字符串放到redis中去,然后前端某个服务要取这个session的时候也会自动的redis中取
注意:由于这里使用springsession的用的类型是redis,所以这springsession和redis都要一起加入依赖和配置(所以session会被存到Redis缓存中)