文章目錄
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);
}
}