• 登录业务实现(单点登录+微信扫码+短信服务)


    目录

    登录业务的介绍

    1.首先是最早的单一服务器模式

     2.SSO模式

    3.Token模式

    用户登录注册接口实现

    1.创建LoginVo和RegisterVo作为前端展示数据的封装

    2.控制层编写登录和注册方法

    4.统计某天注册人数

    短信服务

    JWT工具类

    登录之后token的拦截检验可以弄一个JWT过滤器


     

    登录业务的介绍

    1.首先是最早的单一服务器模式

    实现:使用session,登录成功后将用户数据放到session中,判断是否登录,拦截器判断session中是否有数据即可

    缺点:单点性能压力太大,而且不能扩展

     2.SSO模式

    场景:也就是分布式模式,当我们用户在一台服务器上进行认证之后,那么其他服务器怎么知道它是否认证呢?我们可以将认证数据存储到缓存中,其他服务器从缓存中获取即可

    再说说图情况,采用微服务思想,分布式部署模式,认证中心在一台服务器上,对用户数据库操作以及用户缓存中心,然后我们的业务是否能做直接feign我们的认证中心业务即可

    实现:session广播机制,也就是session复制,或者cookie+redis也就是下面这个,登录之后将数据放到redis中,kv为用户数据,并且把redis中的key放在cookie中,当访问其他模块的时候会携带cookie进行发送,获取cookie值对redis进行查询

     优点:

    1.用户信息单独管理,更好的分布式惯例

    2.可以自己扩展安全策略

    缺点:认证服务器压力较大

    3.Token模式

     单点模式常见处理方式,就是使用token实现,在项目登录后,会按照我们JWT的规则生成字符串,把登录之后用户字符串返回(1.可以放在cookie,2.也可以请求头中),一般验证我们会将携带的token解析——>userId或者其他标识然后判断redis中有数据

    (34条消息) Security思想项目总结_Fairy要carry的博客-CSDN博客

    优点:

    无状态: token无状态,session有状态的

    基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT)

    缺点:

    占用带宽

    无法在服务器端销毁

    注:基于微服务开发,选择token的形式相对较多,因此我使用token作为用户认证的标准

    用户登录注册接口实现

    1.创建LoginVo和RegisterVo作为前端展示数据的封装

    1. @Data
    2. @ApiModel(value="登录对象", description="登录对象")
    3. public class LoginVo {
    4. @ApiModelProperty(value = "手机号")
    5. private String mobile;
    6. @ApiModelProperty(value = "密码")
    7. private String password;
    8. }
    1. @Data
    2. @ApiModel(value="注册对象", description="注册对象")
    3. public class RegisterVo {
    4. @ApiModelProperty(value = "昵称")
    5. private String nickname;
    6. @ApiModelProperty(value = "手机号")
    7. private String mobile;
    8. @ApiModelProperty(value = "密码")
    9. private String password;
    10. @ApiModelProperty(value = "验证码")
    11. private String code;
    12. }

    2.控制层编写登录和注册方法

    登录成功之后返回token

    1. /**
    2. *

    3. * 会员表 前端控制器
    4. *

    5. *
    6. * @author testjava
    7. * @since 2022-07-19
    8. */
    9. @RestController
    10. @RequestMapping("/educenter/member")
    11. @CrossOrigin
    12. @Api(description = "登录注册业务")
    13. public class UcenterMemberController {
    14. @Autowired
    15. private UcenterMemberService memberService;
    16. /**
    17. * 1.登录
    18. * 验证验证码是否正确返回token
    19. */
    20. @ApiOperation(value = "登录测试接口")
    21. @PostMapping("login")
    22. public R loginUser(@RequestBody UcenterMember member){
    23. //调用service验证
    24. String token=memberService.login(member);
    25. return R.ok().data("token",token);
    26. }
    27. /**
    28. * 2.会员注册
    29. */
    30. @ApiOperation(value = "注册测试接口")
    31. @PostMapping("register")
    32. public R register(@RequestBody RegisterVo registerVo){
    33. memberService.register(registerVo);
    34. return R.ok();
    35. }

    3.业务层实现

    登录思路:1.首先从UcenterMember中获取手机号和密码,并且对其判空——>2.然后我们需要判断手机号和密码是否正确,将这两个作为条件封装到Wrapper中进行select查询——>3.对查询出来的数据与传入的人物信息进行比较——>4.如果都没问题,则取出传入用户信息的ID和Name,结合JWT做一个签名生成token

    注册思路:1.首先获取注册中的字段属性值(昵称+手机号+密码+验证码),然后进行校验参数判断是否为空——>2.首先校验验证码,我们验证码是被存在redis中,从中获取判断输入的code是否一致——>3.然后判断手机号是否重复——>4.没问题就new UcenterMember,将以上注册的数据封装到人物类中,然后插入进数据库

    1. package com.atguigu.educms.service.impl;
    2. import com.atguigu.educms.entity.UcenterMember;
    3. import com.atguigu.educms.entity.vo.RegisterVo;
    4. import com.atguigu.educms.mapper.UcenterMemberMapper;
    5. import com.atguigu.educms.service.UcenterMemberService;
    6. import com.atguigu.eduservice.JwtUtils;
    7. import com.atguigu.eduservice.MD5;
    8. import com.atguigu.eduservice.exceptionHandler.GuliException;
    9. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    10. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    11. import org.apache.commons.lang.StringUtils;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.data.redis.core.RedisTemplate;
    14. import org.springframework.stereotype.Service;
    15. /**
    16. *

    17. * 会员表 服务实现类
    18. *

    19. *
    20. * @author testjava
    21. * @since 2022-07-19
    22. */
    23. @Service
    24. public class UcenterMemberServiceImpl extends ServiceImpl implements UcenterMemberService {
    25. @Autowired
    26. private RedisTemplate redisTemplate;
    27. /**
    28. * 1.登录返回token验证,并且放入redis,一定时间后失效
    29. * 先对手机号和密码进行非空判断,然后根据手机号查询数据库数据
    30. * 再对数据库数据进行判断,最后根据数据中id与nickname进行token验证
    31. *
    32. * @return
    33. */
    34. @Override
    35. public String login(UcenterMember member) {
    36. //1.1获取手机号和密码
    37. String mobile = member.getMobile();
    38. String password = member.getPassword();
    39. //1.2手机和密码非空判断
    40. if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
    41. throw new GuliException(20001, "登录失败");
    42. }
    43. //1.2判断手机号是否正确
    44. QueryWrapper wrapper = new QueryWrapper<>();
    45. wrapper.eq("mobile", mobile);
    46. UcenterMember mobileMember = baseMapper.selectOne(wrapper);//根据手机号得到member
    47. //1.3对查询的member进行判断
    48. if (mobileMember == null) {
    49. throw new GuliException(20001, "登录失败");
    50. }
    51. //1.4不为空判断密码
    52. if (!MD5.encrypt(password).equals(mobileMember.getPassword())) {
    53. throw new GuliException(20001, "登录失败");
    54. }
    55. //1.5判断用户是否被封号
    56. if (mobileMember.getIsDisabled()) {
    57. throw new GuliException(20001, "登录失败");
    58. }
    59. //1.6生成Jwt的签名token——>根据前端传来的member所根据数据库查询到的数据进行token生成
    60. String token = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
    61. return token;
    62. }
    63. /**
    64. * 2.注册的业务方法
    65. *
    66. * @param registerVo
    67. */
    68. @Override
    69. public void register(RegisterVo registerVo) {
    70. //2.1.首先获取前端注册表单里面的数据
    71. String nickname = registerVo.getNickname();
    72. String mobile = registerVo.getMobile();//手机号
    73. String password = registerVo.getPassword();
    74. String code = registerVo.getCode();//验证码
    75. //2.2校验参数
    76. if (StringUtils.isEmpty(mobile) ||
    77. StringUtils.isEmpty(mobile) ||
    78. StringUtils.isEmpty(password) ||
    79. StringUtils.isEmpty(code)) {
    80. throw new GuliException(20001, "注册失败");
    81. }
    82. //2.3校验验证码,先从redis中取出来,然后进行判断
    83. String redis_code = redisTemplate.opsForValue().get(mobile);
    84. if (!redis_code.equals(code)) {
    85. throw new GuliException(20001, "注册失败,验证码失效");
    86. }
    87. //2.4判断手机号是否重复->在数据库中进行查询mobile即可
    88. Integer count = baseMapper.selectCount(new QueryWrapper().eq("mobile", mobile));
    89. if (count.intValue() > 0) {
    90. throw new GuliException(20001, "注册失败,手机号出现重复");
    91. }
    92. //2.5添加到数据库中
    93. UcenterMember member = new UcenterMember();
    94. member.setNickname(nickname);
    95. member.setMobile(mobile);
    96. member.setPassword(MD5.encrypt(password));
    97. member.setIsDisabled(false);
    98. member.setAvatar("http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132");
    99. baseMapper.insert(member);
    100. }
    101. /**
    102. * 3,根据openid查询member是否存在
    103. * @param openid
    104. * @return
    105. */
    106. @Override
    107. public UcenterMember getOpenIdMember(String openid) {
    108. QueryWrapper wrapper = new QueryWrapper<>();
    109. wrapper.eq("openid",openid);
    110. UcenterMember member = baseMapper.selectOne(wrapper);
    111. return member;
    112. }
    113. /**
    114. * 4.根据天数得到注册人数
    115. * @param day
    116. * @return
    117. */
    118. @Override
    119. public Integer countRegister(String day) {
    120. return baseMapper.countRegisterDay(day);
    121. }
    122. }

    4.统计某天注册人数

    1. /**
    2. * 4.根据天数得到注册人数
    3. * @param day
    4. * @return
    5. */
    6. @Override
    7. public Integer countRegister(String day) {
    8. return baseMapper.countRegisterDay(day);
    9. }
    1. mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    2. <mapper namespace="com.atguigu.educms.mapper.UcenterMemberMapper">
    3. <select id="countRegisterDay" resultType="java.lang.Integer">
    4. SELECT COUNT(*) FROM ucenter_member uc
    5. WHERE DATE (uc.`gmt_create`)=#{day}
    6. select>
    7. mapper>

    短信服务

    这里我们用的是第三方接口,短信服务这里应用在注册,注册必须输入验证码,与redis中验证码作校验

    思路:1.首先我们先从redis中根据phone获取验证码,如果不为空就直接返回(因为redis中有对应的验证码了)——>2.否则生成四位随机验证码——>3.怎么发送直接调用接口,你的appcode,smsSignId,templateId——>4.发送后我们将值存入redis并且设置存活日期

    1. package com.atguigu.msmservice.controller;
    2. import com.atguigu.eduservice.R;
    3. import com.atguigu.msmservice.config.HttpUtils;
    4. import com.atguigu.msmservice.config.RandomUtil;
    5. import com.atguigu.msmservice.service.MsmService;
    6. import io.swagger.annotations.Api;
    7. import io.swagger.annotations.ApiOperation;
    8. import org.apache.commons.lang.StringUtils;
    9. import org.apache.http.HttpResponse;
    10. import org.apache.http.util.EntityUtils;
    11. import org.springframework.beans.factory.annotation.Autowired;
    12. import org.springframework.data.redis.core.RedisTemplate;
    13. import org.springframework.web.bind.annotation.*;
    14. import java.util.HashMap;
    15. import java.util.Map;
    16. import java.util.concurrent.TimeUnit;
    17. @RestController
    18. @RequestMapping("/edumsm/msm")
    19. @CrossOrigin
    20. @Api(description = "根据phone验证码发送")
    21. public class MsmController {
    22. @Autowired
    23. private MsmService msmService;
    24. @Autowired
    25. private RedisTemplate redisTemplate;
    26. /**
    27. * 1.发送短信接口
    28. * @param phone
    29. * @return
    30. */
    31. @ApiOperation(value = "发送验证码的接口")
    32. @GetMapping("send/{phone}")
    33. public R sendMsm(@PathVariable String phone){
    34. //1.从redis中获取验证码,如果获取到就直接返回
    35. String code = redisTemplate.opsForValue().get(phone);
    36. if(!StringUtils.isEmpty(code)){
    37. return R.ok();
    38. }
    39. //2.生成随机4位数验证码
    40. code = RandomUtil.getFourBitRandom();
    41. String host = "https://gyytz.market.alicloudapi.com";
    42. String path = "/sms/smsSend";
    43. String method = "POST";
    44. String appcode = "xxxx";
    45. Map headers = new HashMap();
    46. //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
    47. headers.put("Authorization", "APPCODE " + appcode);
    48. Map querys = new HashMap();
    49. querys.put("mobile", phone);
    50. querys.put("param", "**code**:"+code+",**minute**:5");
    51. querys.put("smsSignId", "xxxx");
    52. querys.put("templateId", "xxxx");
    53. Map bodys = new HashMap();
    54. try {
    55. /**
    56. * 重要提示如下:
    57. * HttpUtils请从
    58. * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
    59. * 下载
    60. *
    61. * 相应的依赖请参照
    62. * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
    63. */
    64. HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
    65. System.out.println(response.toString());
    66. //获取response的body
    67. System.out.println(EntityUtils.toString(response.getEntity()));
    68. /**
    69. * 3.将值存入缓存redis中
    70. */
    71. redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
    72. return R.ok();
    73. } catch (Exception e) {
    74. e.printStackTrace();
    75. return R.error();
    76. }
    77. }
    78. }

    JWT工具类

    1. import io.jsonwebtoken.Claims;
    2. import io.jsonwebtoken.Jws;
    3. import io.jsonwebtoken.Jwts;
    4. import io.jsonwebtoken.SignatureAlgorithm;
    5. import org.springframework.util.StringUtils;
    6. import javax.servlet.http.HttpServletRequest;
    7. import java.util.Date;
    8. /**
    9. * @author
    10. */
    11. public class JwtUtils {
    12. public static final long EXPIRE = 1000 * 60 * 60 * 24;
    13. public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
    14. public static String getJwtToken(String id, String nickname){
    15. String JwtToken = Jwts.builder()
    16. .setHeaderParam("typ", "JWT")
    17. .setHeaderParam("alg", "HS256")
    18. .setSubject("guli-user")
    19. .setIssuedAt(new Date())
    20. .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
    21. .claim("id", id)
    22. .claim("nickname", nickname)
    23. .signWith(SignatureAlgorithm.HS256, APP_SECRET)
    24. .compact();
    25. return JwtToken;
    26. }
    27. /**
    28. * 判断token是否存在与有效
    29. * @param jwtToken
    30. * @return
    31. */
    32. public static boolean checkToken(String jwtToken) {
    33. if(StringUtils.isEmpty(jwtToken)) return false;
    34. try {
    35. Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
    36. } catch (Exception e) {
    37. e.printStackTrace();
    38. return false;
    39. }
    40. return true;
    41. }
    42. /**
    43. * 判断token是否存在与有效
    44. * @param request
    45. * @return
    46. */
    47. public static boolean checkToken(HttpServletRequest request) {
    48. try {
    49. String jwtToken = request.getHeader("token");
    50. if(StringUtils.isEmpty(jwtToken)) return false;
    51. Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
    52. } catch (Exception e) {
    53. e.printStackTrace();
    54. return false;
    55. }
    56. return true;
    57. }
    58. /**
    59. * 根据token获取会员id
    60. * @param request
    61. * @return
    62. */
    63. public static String getMemberIdByJwtToken(HttpServletRequest request) {
    64. String jwtToken = request.getHeader("token");
    65. if(StringUtils.isEmpty(jwtToken)) return "";
    66. Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
    67. Claims claims = claimsJws.getBody();
    68. return (String)claims.get("id");
    69. }
    70. }

    登录之后token的拦截检验可以弄一个JWT过滤器

    (34条消息) SpringSecurity前后端分离应用_Fairy要carry的博客-CSDN博客_springsecurity实现前后端分离

    (34条消息) SpringSecurity前后端分离02(授权)_Fairy要carry的博客-CSDN博客

  • 相关阅读:
    CRM系统的客户细分有什么作用?
    NBA球员数据爬虫练习
    云计算发展
    第2关:BeautifulSoup解析网页
    JS数据类型判断方式总结
    SpringCloudGateway获取报文大小
    人工智能轨道交通行业周刊-第61期(2023.9.18-9.24)
    esp32如何获得蓝牙地址
    深入Linux:权限管理与常用命令详解
    JSAPI实现H5页面微信支付
  • 原文地址:https://blog.csdn.net/weixin_57128596/article/details/126082340