天天看點

使用者登入、代碼優化

文章目錄

7、使用者登入

7.1、登入驗證碼

7.2、JWT

7.3、使用者登入

8、代碼優化

8.1 抽取BasePojo

8.2 自動填充

7、使用者登入

7.1、登入驗證碼

使用者登入、代碼優化

7.1.1、接口說明

使用者登入、代碼優化

參見YAPI接口位址:http://192.168.136.160:3000/project/19/interface/api/94

7.1.2、流程分析

使用者登入、代碼優化

用戶端發送請求

服務端調用第三方元件發送驗證碼

驗證碼發送成功,存入redis

響應用戶端,用戶端跳轉到輸入驗證碼頁面

7.1.3、代碼實作

LoginController

@RestController
@RequestMapping("/user")
public class LoginController {

    @Autowired
    private UserService userService;

    /**
     * 擷取登入驗證碼
     *   請求參數:phone (Map)
     *   響應:void
     */
    @PostMapping("/login")
    public ResponseEntity login(@RequestBody Map map){
        String phone =(String) map.get("phone");
        userService.sendMsg(phone);
        return ResponseEntity.ok(null); //正常傳回狀态碼200
    }
}           

UserService

@Service
public class UserService {

    @Autowired
    private SmsTemplate template;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 發送短信驗證碼
     * @param phone
     */
    public void sendMsg(String phone) {
        //1、随機生成6位數字
        //String code = RandomStringUtils.randomNumeric(6);
        String code = "123456";
        //2、調用template對象,發送手機短信
        //template.sendSms(phone,code);
        //3、将驗證碼存入到redis
        redisTemplate.opsForValue().set("CHECK_CODE_"+phone,code, Duration.ofMinutes(5));
    }
 }           

7.2、JWT

7.2.1、簡介

JSON Web token簡稱JWT, 是用于對應用程式上的使用者進行身份驗證的标記。也就是說, 使用 JWTS 的應用程式不再需要儲存有關其使用者的 cookie 或其他session資料。此特性便于可伸縮性, 同時保證應用程式的安全

7.2.2、格式

  • JWT就是一個字元串,經過加密處理與校驗處理的字元串,形式為:A.B.C
  • A由JWT頭部資訊header加密得到
  • B由JWT用到的身份驗證資訊json資料加密得到
  • C由A和B加密得到,是校驗部分
使用者登入、代碼優化

7.2.3、流程

使用者登入、代碼優化

7.2.4、示例

導入依賴:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>           

編寫測試用例:

@Test
    public void testCreateToken() {
        //生成token
        //1、準備資料
        Map map = new HashMap();
        map.put("id",1);
        map.put("mobile","13800138000");
        //2、使用JWT的工具類生成token
        long now = System.currentTimeMillis();
        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, "itcast") //指定加密算法
                .setClaims(map) //寫入資料
                .setExpiration(new Date(now + 30000)) //失效時間
                .compact();
        System.out.println(token);
    }

    //解析token

    /**
     * SignatureException : token不合法
     * ExpiredJwtException:token已過期
     */
    @Test
    public void testParseToken() {
        String token = "eyJhbGciOiJIUzUxMiJ9.eyJtb2JpbGUiOiIxMzgwMDEzODAwMCIsImlkIjoxLCJleHAiOjE2MTgzOTcxOTV9.2lQiovogL5tJa0px4NC-DW7zwHFqZuwhnL0HPAZunieGphqnMPduMZ5TtH_mxDrgfiskyAP63d8wzfwAj-MIVw";
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey("itcast")
                    .parseClaimsJws(token)
                    .getBody();
            Object id = claims.get("id");
            Object mobile = claims.get("mobile");
            System.out.println(id + "--" + mobile);
        }catch (ExpiredJwtException e) {
            System.out.println("token已過期");
        }catch (SignatureException e) {
            System.out.println("token不合法");
        }

    }           

通過解析Token得知,如果抛出SignatureException異常表示token不合法,如果抛出ExpiredJwtException異常表示token已過期

7.2.5 JWT工具類

public class JwtUtils {

    // TOKEN的有效期1小時(S)
    private static final int TOKEN_TIME_OUT = 1 * 3600;

    // 加密KEY
    private static final String TOKEN_SECRET = "itcast";


    // 生成Token
    public static String getToken(Map params){
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) //加密方式
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //過期時間戳
                .addClaims(params)
                .compact();
    }


    /**
     * 擷取Token中的claims資訊
     */
    public static Claims getClaims(String token) {
        return Jwts.parser()
                .setSigningKey(TOKEN_SECRET)
                .parseClaimsJws(token).getBody();
    }


    /**
     * 是否有效 true-有效,false-失效
     */
    public static boolean verifyToken(String token) {
      
        if(StringUtils.isEmpty(token)) {
            return false;
        }
        
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey("itcast")
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e) {
            return false;
        }

      return true;
    }
}           

7.3、使用者登入

使用者接收到驗證碼後,進行輸入驗證碼,點選登入,前端系統将手機号以及驗證碼送出到服務端進行校驗。

使用者登入、代碼優化

7.3.1、接口文檔

使用者登入、代碼優化

YAPI接口位址:YApi-高效、易用、功能強大的可視化接口管理平台

7.3.2、LoginController

/**
     * 檢驗登入
     */
    @PostMapping("/loginVerification")
    public ResponseEntity loginVerification(@RequestBody Map map) {
        //1、調用map集合擷取請求參數
        String phone = (String) map.get("phone");
        String code = (String) map.get("verificationCode");
        //2、調用userService完成使用者登入
        Map retMap = userService.loginVerification(phone,code);
        //3、構造傳回
        return ResponseEntity.ok(retMap);
    }           

7.3.3、UserService

/**
     * 驗證登入
     * @param phone
     * @param code
     */
    public Map loginVerification(String phone, String code) {
        //1、從redis中擷取下發的驗證碼
        String redisCode = redisTemplate.opsForValue().get("CHECK_CODE_" + phone);
        //2、對驗證碼進行校驗(驗證碼是否存在,是否和輸入的驗證碼一緻)
        if(StringUtils.isEmpty(redisCode) || !redisCode.equals(code)) {
            //驗證碼無效
             throw new RuntimeException();
        }
        //3、删除redis中的驗證碼
        redisTemplate.delete("CHECK_CODE_" + phone);
        //4、通過手機号碼查詢使用者
        User user = userApi.findByMobile(phone);
        boolean isNew = false;
        //5、如果使用者不存在,建立使用者儲存到資料庫中
        if(user == null) {
            user = new User();
            user.setMobile(phone);
            user.setPassword(DigestUtils.md5Hex("123456"));
            Long userId = userApi.save(user);
            user.setId(userId);
            isNew = true;
        }
        //6、通過JWT生成token(存入id和手機号碼)
        Map tokenMap = new HashMap();
        tokenMap.put("id",user.getId());
        tokenMap.put("mobile",phone);
        String token = JwtUtils.getToken(tokenMap);
        //7、構造傳回值
        Map retMap = new HashMap();
        retMap.put("token",token);
        retMap.put("isNew",isNew);

        return retMap;
    }           

7.3.4、測試

使用者登入、代碼優化

8、代碼優化

8.1 抽取BasePojo

為了簡化實體類中created和updated字段,抽取BasePojo

@Data
public abstract class BasePojo implements Serializable {

    @TableField(fill = FieldFill.INSERT) //自動填充
    private Date created;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updated;

}           

8.2 自動填充

package com.tanhua.dubbo.server.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        Object created = getFieldValByName("created", metaObject);
        if (null == created) {
            //字段為空,可以進行填充
            setFieldValByName("created", new Date(), metaObject);
        }

        Object updated = getFieldValByName("updated", metaObject);
        if (null == updated) {
            //字段為空,可以進行填充
            setFieldValByName("updated", new Date(), metaObject);
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        //更新資料時,直接更新字段
        setFieldValByName("updated", new Date(), metaObject);
    }
}