需求
一、后端
集成JWT
service-base
模块,添加依赖
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
-
创建
util
包JwtUtils.java
package com.indi.srb.base.util;
public class JwtUtils {
private static long tokenExpiration = 24*60*60*1000;
private static String tokenSignKey = "A1t2g3uigu123456";
private static Key getKeyInstance(){
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] bytes = DatatypeConverter.parseBase64Binary(tokenSignKey);
return new SecretKeySpec(bytes,signatureAlgorithm.getJcaName());
}
public static String createToken(Long userId, String userName) {
String token = Jwts.builder()
.setSubject("SRB-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.claim("userId", userId)
.claim("userName", userName)
.signWith(SignatureAlgorithm.HS512, getKeyInstance())
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
/**
* 判断token是否有效
* @param token
* @return
*/
public static boolean checkToken(String token) {
if(StringUtils.isEmpty(token)) {
return false;
}
try {
Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
public static Long getUserId(String token) {
Claims claims = getClaims(token);
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
public static String getUserName(String token) {
Claims claims = getClaims(token);
return (String)claims.get("userName");
}
public static void removeToken(String token) {
//jwttoken无需删除,客户端扔掉即可。
}
/**
* 校验token并返回Claims
* @param token
* @return
*/
private static Claims getClaims(String token) {
if(StringUtils.isEmpty(token)) {
// LOGIN_AUTH_ERROR(-211, "未登录"),
throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
}
try {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return claims;
} catch (Exception e) {
throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
}
}
}
集成swapper-bootstrap
service-base
模块
<!--swagger-bootstrap-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.2</version>
</dependency>
vo
LoginVO.java
package com.indi.srb.core.pojo.vo;
@Data
@ApiModel(description = "登录对象")
public class LoginVO {
@ApiModelProperty(value = "用户类型")
private Integer userType;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
}
UserInfoVO.java
package com.indi.srb.core.pojo.vo;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description="用户信息对象")
public class UserInfoVO {
@ApiModelProperty(value = "1:出借人 2:借款人")
private Integer userType;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "用户密码")
private String password;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "用户姓名")
private String name;
@ApiModelProperty(value = "用户访问令牌")
private String token;
}
entity
UserLoginRecord.java
public UserLoginRecord(Long userId, String ip) {
this.userId = userId;
this.ip = ip;
}
service
UserInfoService.java
UserInfoVO login(LoginVO loginVO, String ip);
UserInfoServiceImpl.java
@Resource
private UserLoginRecordMapper userLoginRecordMapper;
@Override
public UserInfoVO login(LoginVO loginVO, String ip) {
String mobile = loginVO.getMobile();
String password = loginVO.getPassword();
Integer userType = loginVO.getUserType();
// 用户是否存在
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("mobile", mobile);
queryWrapper.eq("user_type", userType);
UserInfo userInfo = baseMapper.selectOne(queryWrapper);
Assert.notNull(userInfo, ResponseEnum.LOGIN_MOBILE_ERROR);
// 密码是否正确
Assert.equals(MD5.encrypt(password), userInfo.getPassword(), ResponseEnum.LOGIN_PASSWORD_ERROR);
// 用户是否被锁定
Assert.equals(userInfo.getStatus(), UserInfo.STATUS_NORMAL, ResponseEnum.LOGIN_LOCKED_ERROR);
// 记录登录日志
UserLoginRecord userLoginRecord = new UserLoginRecord(userInfo.getId(),ip);
userLoginRecordMapper.insert(userLoginRecord);
String token = JwtUtils.createToken(userInfo.getId(), userInfo.getName());
UserInfoVO userInfoVO = new UserInfoVO(
userType,
mobile,
password,
userInfo.getNickName(),
userInfo.getName(),
token
);
return userInfoVO;
}
controller
UserInfoController.java
@ApiOperation("用户登录")
@PostMapping("/login")
public R login(@RequestBody LoginVO loginVO, HttpServletRequest request) {
String mobile = loginVO.getMobile();
String password = loginVO.getPassword();
Assert.notEmpty(mobile, ResponseEnum.MOBILE_NULL_ERROR);
Assert.notEmpty(password, ResponseEnum.PASSWORD_NULL_ERROR);
UserInfoVO userInfoVO = userInfoService.login(loginVO, request.getRemoteAddr());
return R.ok().setData("userInfo", userInfoVO);
}
@ApiOperation("校验令牌")
@GetMapping("/checkToken")
public R checkToken(HttpServletRequest request) {
// 从请求头中获取token
String token = request.getHeader("token");
// 检查token是否有效
boolean result = JwtUtils.checkToken(token);
if (result) {
return R.ok();
} else {
return R.setResult(ResponseEnum.LOGIN_AUTH_ERROR);
}
}
二、swagger测试
访问
http://localhost:8110/doc.html
添加全局参数
刷新页面,测试
三、前端
登录界面
pages/login.vue
methods: {
//登录
login() {
if (!this.userInfo.mobile) {
this.$message.error('手机号不能为空')
return
}
if (!this.userInfo.password) {
this.$message.error('密码不能为空')
return
}
this.$axios
.$post('/api/core/userInfo/login', this.userInfo)
.then((response) => {
cookie.set('userInfo',response.data.userInfo)
window.location.href='/user'
})
},
},
}
页面头信息
components/AppHeader.vue
methods: {
//显示用户信息
showInfo() {
let userInfo = cookie.get('userInfo')
if (!userInfo) {
console.log('cookie不存在')
this.userInfo = null
return
}
userInfo = JSON.parse(userInfo)
// 需要在请求头中设置token
// this.$axios({
// url: '/api/core/userInfo/checkToken',
// method: 'get',
// headers: {
// token: userInfo.token,
// },
// }).then((response) => {
// this.userInfo = userInfo
// })
// 后面添加了拦截器,可以不用在请求头中设置
this.$axios.$get('/api/core/userInfo/checkToken').then((response) => {
this.userInfo = userInfo
})
},
//退出
logout() {
cookie.set('userInfo', '')
window.location.href = '/login'
},
},
axios拦截器
plugins/axios.js
统一添加header,这样就不需要每次向后端请求的时候添加token了
import { Message } from 'element-ui'
import cookie from 'js-cookie'
export default function({ $axios, redirect }) {
// 请求拦截
$axios.onRequest((config) => {
// 添加请求头:token
// 这样就不需要每次向后端请求的时候添加token了
let userInfo = cookie.get('userInfo')
if (userInfo) {
// debugger
userInfo = JSON.parse(userInfo)
config.headers['token'] = userInfo.token
}
console.log('Making request to ' + config.url)
})
// 请求失败
$axios.onRequestError((error) => {
console.log('onRequestError', error) // for debug
})
// 响应拦截:处理未登录状况
$axios.onResponse((response) => {
console.log('Reciving resposne', response)
if (response.data.code === 0) {
return response
} else if (response.data.code === -211) {
console.log('用户校验失败')
// debugger
cookie.set('userInfo', '')
window.location.href = '/'
} else {
Message({
message: response.data.message,
type: 'error',
duration: 5 * 1000,
})
return Promise.reject(response)
}
})
// 通信失败
$axios.onResponseError((error) => {
console.log('onResponseError', error) // for debug
})
}