1.导入依赖
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2.创建TokenUtils
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.ethan.exam_ms.entity.User;
import com.ethan.exam_ms.exception.ServiceException;
import java.util.Calendar;
import java.util.Date;
public class TokenUtils {
//密钥
public static final String SECRET = "areyoucrazy?pojie?bukenengde?";
//过期时间:秒
public static final int EXPIRE = 30;
public static String getToken(String id,String username){
Calendar nowTime = Calendar.getInstance();
//过期时间
nowTime.add(Calendar.SECOND, EXPIRE);
Date expireDate = nowTime.getTime();
String token = JWT.create()
//这是在设置第二部分信息,不要设置密码之类的,因为这些信息可以通过浏览器获取
//用户id
.withClaim("id", id)
//用户名
.withClaim("username",username)
//创建token的时间
.withIssuedAt(new Date())//签名时间
//设置token的过期时间
.withExpiresAt(expireDate)//过期时间
//设置第一部分
.sign(Algorithm.HMAC256(SECRET));//签名
return token;
}
/**
* 验证token合法性 成功返回token
*/
public static DecodedJWT verify(String token) throws ServiceException {
if(StrUtil.isBlank(token)){
throw new ServiceException("token不能为空");
}
JWTVerifier build = JWT.require(Algorithm.HMAC256(SECRET)).build();
return build.verify(token);
}
}
3.登录拦截器
(1)没有统一返回类,用map封装
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.ethan.exam_ms.exception.ServiceException;
import com.ethan.exam_ms.utils.TokenUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String,Object> map=new HashMap<>();
//从请求中获得token
String token = request.getHeader("token");
if(!(handler instanceof HandlerMethod)){
return true;
}
if(StrUtil.isEmpty(token)){
throw new ServiceException("NOTLOGIN");
}
try {
TokenUtils.verify(token);
return true;
} catch (SignatureVerificationException e) {
map.put("msg","无效签名! 错误 ->");
} catch (TokenExpiredException e) {
map.put("msg","token过期! 错误 ->");
} catch (AlgorithmMismatchException e) {
map.put("msg","token算法不一致! 错误 ->");
} catch (Exception e) {
map.put("msg","token无效! 错误 ->");
}
map.put("state",false);
//设置响应
String json=new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
(2)有统一返回类,用R
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.ethan.exam_ms.common.R;
import com.ethan.exam_ms.exception.ServiceException;
import com.ethan.exam_ms.utils.TokenUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String erroMsg=null;
//从请求中获得token
String token = request.getHeader("token");
if(!(handler instanceof HandlerMethod)){
return true;
}
if(StrUtil.isEmpty(token)){
throw new ServiceException("NOTLOGIN");
}
try {
TokenUtils.verify(token);
return true;
} catch (SignatureVerificationException e) {
erroMsg="无效签名! 错误 ";
} catch (TokenExpiredException e) {
erroMsg="token过期! 错误 ";
} catch (AlgorithmMismatchException e) {
erroMsg="token算法不一致! 错误 ";
} catch (Exception e) {
erroMsg="token无效! 错误";
}
String json=new ObjectMapper().writeValueAsString(R.error(erroMsg));
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
4.配置拦截器
import com.ethan.exam_ms.common.JacksonObjectMapper;
import com.ethan.exam_ms.interceptor.LoginInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
//排除不需要拦截的请求
.excludePathPatterns(
"/manager/login",
"/user/login",
"/login",
"/manager/logout"
);
}
}
5.Controller里使用
UserController
/**
* 用户登录
* @param loginDto
* @return
*/
@PostMapping("login")
public R<LoginDto> toLogin(HttpServletRequest request, @RequestBody LoginDto loginDto){
log.info("登录名:{},登录密码:{}",loginDto.getUsername(),loginDto.getPassword());
String username = loginDto.getUsername();
String password = loginDto.getPassword();
//1.判断输入值是否为空
if(StringUtils.isBlank(username)||StringUtils.isBlank(password)){
return R.error("用户名或密码不可为空");
}
LoginDto login = userService.login(loginDto);
return R.success(login);
}
UserServiceImpl
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ethan.exam_ms.dto.LoginDto;
import com.ethan.exam_ms.entity.Manager;
import com.ethan.exam_ms.entity.User;
import com.ethan.exam_ms.exception.ServiceException;
import com.ethan.exam_ms.mapper.UserMapper;
import com.ethan.exam_ms.service.UserService;
import com.ethan.exam_ms.utils.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
public LoginDto login(LoginDto loginDto) {
String password = loginDto.getPassword();
//1.对密码进行md5加密
password= DigestUtils.md5DigestAsHex(password.getBytes());
log.info("加密后的密码:{}",password);
User one = getUserInfo(loginDto);
if (one != null) {
BeanUtil.copyProperties(one, loginDto, true);
// 设置token
String token = TokenUtils.getToken(one.getId().toString(),one.getNickname());
loginDto.setToken(token);
return loginDto;
} else {
throw new ServiceException("用户名或密码错误");
}
}
public User getUserInfo(LoginDto loginDto) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getNickname, loginDto.getUsername());
queryWrapper.eq(User::getPassword, DigestUtils.md5DigestAsHex(loginDto.getPassword().getBytes()));
User one;
try {
one = getOne(queryWrapper); // 从数据库查询用户信息
} catch (Exception e) {
//这个地方需要写个全局异常处理类,自定义一个ServiceException方法来处理异常
throw new ServiceException("系统错误");
}
return one;
}
}
其他UserService,mapper文件使用了mybatis-plus较为简单省略