项目二02

1.介绍

  • 参数校验
  • 统一异常
  • key值优化
  • 发送短信
  • 登录实现

2.参数校验

2.1.步骤

//参数判空

//手机号码正则

//密码是否一致

//验证码是否一致

//手机号码唯一

//用户注册

2.2.工具类

//工具类的方法都是静态的,传入的参数最好做判空处理
public class AssertUtil {
    private AssertUtil(){

    }

    public static void hasText(String text,String msg){
        if(!StringUtils.hasText(text)){
            throw new LogicException(msg);
        }
    }

    public static void isEquals(String v1,String v2,String msg){
        if(v1 == null || v2==null){
            throw new LogicException("传入参数必须有值");
        }

        if(!v1.equals(v2)){
            throw new LogicException(msg);
        }
    }

}

2.3.参考代码

    //下面抛出的异常设计到统一异常处理
    public void regist(String nickname, String password, String rpassword, String phone, String verifyCode) {
        //判断参数是否为null
        AssertUtil.hasText(nickname, "昵称不能为空");
        AssertUtil.hasText(password, "密码不能为空");
        AssertUtil.hasText(rpassword, "认证密码不能为空");
        AssertUtil.hasText(phone, "手机号码不能为空");
        AssertUtil.hasText(verifyCode, "验证码不能为空");

        //判断密码是否一致
        AssertUtil.isEquals(password, rpassword, "两次密码不一致");

        //手机格式是否符合格式

        //手机号码是否唯一
        if (this.checkPhone(phone)) {
            throw new LogicException("手机号码已经被注册了");
        }

        //验证码
        String code = userInfoRedisSerivce.getValueByKey(phone);
        if (!verifyCode.equalsIgnoreCase(code)) {
            throw new LogicException("验证码失效或者错误");
        }
        //用户注册
        UserInfo userInfo = new UserInfo();
        userInfo.setNickname(nickname);
        userInfo.setPhone(phone);
        userInfo.setPassword(password);
        userInfo.setState(UserInfo.STATE_NORMAL);
        userInfo.setHeadImgUrl("/images/default.jpg");
        super.save(userInfo);
    }

3.统一异常

异常分为主动异常(自己抛出)和系统异常
项目处理自己的主动异常,剩下的为系统异常
异常类

public class LogicException extends RuntimeException {

    public LogicException(String message) {
        super(message);
    }
}

返回的JsonResult对象

@Setter
@Getter
@NoArgsConstructor
public class JsonResult<T> {
    public static final int CODE_SUCCESS = 200;
    public static final String MSG_SUCCESS = "操作成功";

    public static final int CODE_NOLOGIN = 401;
    public static final String MSG_NOLOGIN = "请先登录";

    public static final int CODE_ERROR = 500;
    public static final String MSG_ERROR = "系统异常,请联系管理员";

    public static final int CODE_ERROR_PARAM = 501;  //参数异常

    private int code;  //区分不同结果, 而不再是true或者false
    private String msg;
    private T data;  //除了操作结果之后, 还行携带数据返回

    public JsonResult(int code, String msg, T data){
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public static <T> JsonResult success(T data){
        return new JsonResult(CODE_SUCCESS, MSG_SUCCESS, data);
    }

    public static JsonResult success(){
        return new JsonResult(CODE_SUCCESS, MSG_SUCCESS, null);
    }

    public static <T>  JsonResult error(int code, String msg, T data){
        return new JsonResult(code, msg, data);
    }

    public static JsonResult defaultError(){
        return new JsonResult(CODE_ERROR, MSG_ERROR, null);
    }


    public static JsonResult noLogin() {
        return new JsonResult(CODE_NOLOGIN, MSG_NOLOGIN, null);
    }
}

正常情况下

@PostMapping("/regist")
    //参数有手机号码和验证码,昵称,密码,确认密码
    public JsonResult regist(String nickname, String password, String rpassword, String phone, String verifyCode) {
        try {
            userInfoService.regist(nickname, password, rpassword, phone, verifyCode);
        } catch (LogicException e) {
            e.printStackTrace();
            //这里出的异常是主动抛出的异常
            return JsonResult.error(JsonResult.CODE_ERROR_PARAM, e.getMessage(), null);
        } catch (RuntimeException e) {
            //这里抛出的异常就是除了主动抛出异常之外的异常
            return JsonResult.defaultError();
        }
        return JsonResult.success();
    }

优化后的异常

@PostMapping("/regist")
    //参数有手机号码和验证码,昵称,密码,确认密码
    public JsonResult regist(String nickname, String password, String rpassword, String phone, String verifyCode) {
        userInfoService.regist(nickname, password, rpassword, phone, verifyCode);
        return JsonResult.success();
    }


优化思路:统一异常的思路有点类似AOP思想,但是真实实现不是AOP,因为使用注解的后置增强

@ControllerAdvice
public class CommonAdminException {
    @ExceptionHandler(LogicException.class)
    @ResponseBody
    public Object logicExp(Exception e, HttpServletResponse resp) {
        e.printStackTrace();
        resp.setContentType("application/json;charset=utf-8");
        return JsonResult.error(JsonResult.CODE_ERROR_PARAM, e.getMessage(), null);
    }

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public Object  runTimeExp(Exception e, HttpServletResponse resp) {
        e.printStackTrace();
        resp.setContentType("application/json;charset=utf-8");
        return JsonResult.defaultError();
    }
}

4.key值优化

为了方便管理redis中的多个key,将key分别加上前缀和拼接字符
枚举类设计

public enum RedisKeys {

    USER_LOGIN_TOKEN("user_login_token",Consts.USER_INFO_TOKEN_VAI_TIME * 60L),
    REGIST_VERIFY_CODE("regist_verify_code", Consts.VERIFY_CODE_VAI_TIME * 60L);

    private String preName;
    private Long time;

    RedisKeys() {
    }

    RedisKeys(String preName, Long time) {
        this.preName = preName;
        this.time = time;
    }

    //拼接完整的key
    public String join(String value){
        StringBuilder sb = new StringBuilder(80);
        sb.append(this.preName).append(":").append(value);

        return sb.toString();
    }

}

使用key值

@Override
    public void setRedisStringValue(String phone, String code) {

        //phone="regit_code:"+phone;
        String key= RedisKeys.REGIST_VERIFY_CODE.join(phone);
        //键,值,时间,单位
        redisTemplate.opsForValue().set(key,code, RedisKeys.REGIST_VERIFY_CODE.getTime(), TimeUnit.SECONDS);
    }

    @Override
    public String getValueByKey(String phone) {
        //String s = redisTemplate.opsForValue().get("regit_code:"+phone);
        String key = RedisKeys.REGIST_VERIFY_CODE.join(phone);
        return redisTemplate.opsForValue().get(key);
    }

注意:

5.发送短信

5.1.介绍


短信的唯一标识

短信平台 京东万象

多个项目的交互可以通过http的方式和socket方式,spring封装了RestTemplate可以方便两个服务之间进行交互

5.2.实现

@Override
    public void sendVerifyCode(String phone) {
        //创建UUID
        String uuid = UUID.randomUUID().toString().substring(0, 4).replaceAll("-","");

        //这里少了拼接
        StringBuilder stringBuilderUuid = new StringBuilder(80);
        stringBuilderUuid.append("你的注册码是:").append(uuid).append("请在").append(Consts.VERIFY_CODE_VAI_TIME).append("之内使用");

        //发送UUID
        System.out.println(stringBuilderUuid.toString());

        //发送短信
        //spring封装的http请求工具类
        RestTemplate template = new RestTemplate();

        //地址,返回的接收类型,电话号码,验证码,标识符
        String ret = template.getForObject(url,String.class,phone,uuid,appkey);

        //将短信的返回信息发送到控制台中
        System.out.println(ret);

        if(!ret.contains("Success")){
            throw new LogicException("短信发送失败");
        }

        //缓存管理
        userInfoRedisSerivce.setRedisStringValue(phone, uuid);
    }


发送短信的业务需要有依赖支持


查询短信支持的方式


除了可以将京东万象返回的东西以字符串显示之外,还可以以一个map的方式返回


如果是map的话需要注意如何获取“Success”,因为字符串可以使用contains的方式


xml同理,也是转换为字符串,判断是否存在“Success”


如果字符串中没有“Success”则会报错,很多短信会失效,所以具体看控制台打印的参数为准

5.3.路径优化

可以将参数写在配置文件中

获取资源文件的内容

@Value("${sms.url}")
private String url


内容可能要注意中括号【】,某种标签
springboot中的url中不支持中文,需要转换为ASCII 中文转ASCII

6.登录实现

6.1.传统方式

服务器通过请求对象获取用户名和密码
根据用户名和密码查询数据库是否存在,并封装成对象
对象如果存在,将对象缓存到Session中,并向前端返回成功的信息
如果为空,提示失败

6.2.前后端分离

因为Session只适用于浏览器,所以适用令牌机制(redis)

操作步骤
服务器通过请求对象获取用户名和密码
根据用户名和密码查询数据库是否存在,并封装成对象
对象如果存在,将token(UUID)和对象设置时效性缓存到redis中,并向前端返回成功的信息,成功的信息又拥有token和对象的json格式
如果为空,提示失败

代码实现

@PostMapping("/login")
    public JsonResult login(String username, String password) {
        //获取对象
        UserInfo userInfo = userInfoService.login(username, password);

        //获取Token,将token和对象数据存入redis中
        String token = userInfoRedisSerivce.getTokenSaveValue(userInfo);

        //返回的数据
        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("token",token);
        hashMap.put("user",userInfo);
        return JsonResult.success(hashMap);
    }

缓存实现

@Override
    public String getTokenSaveValue(UserInfo userInfo) {
        //创建Token
        String token = UUID.randomUUID().toString().replace("-","");

        String key = RedisKeys.USER_LOGIN_TOKEN.join(token);

        String value = JSON.toJSONString(userInfo);

        //存入缓存
        redisTemplate.opsForValue().set(key,value,RedisKeys.USER_LOGIN_TOKEN.getTime());
        String test01 = redisTemplate.opsForValue().get(key);
        System.out.println(test01);

        return token;
    }

6.3.浏览器缓存数据的方式


浏览器其他操作


返回课程体系

posted @ 2021-07-03 09:07  LinkYup  阅读(70)  评论(0编辑  收藏  举报