项目结构
1.导入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!---->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
2.application.properties配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test2
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:mapper/*.xml
3.用户对应数据库的实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
String Id;
String username;
String password;
}
4.dao层
public interface UserMapper {
User findByUsername(@Param("username") String username);
}
不要忘了在主启动类上加接口扫描 @MapperScan(“com.cn.dao”)
5.创建UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.cn.dao.UserMapper">
<select id="findByUsername" resultType="com.cn.entity.User">
SELECT * FROM user
where
username=#{username}
</select>
</mapper>
6.service层
public interface UserService {
User findByUsername(String username);
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
}
7.创建token生成类
//生成token的类
@Service
public class CreateTokenService {
public String createToken(User user){
String token= JWT.create().withAudience(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+1000*30))//token失效的时间 这里我设置的是30秒失效
.withNotBefore(new Date()) //token开始生效的时间
.sign(Algorithm.HMAC256(user.getPassword()));
return token;
}
}
8.jwt配置
token是否验证自定义注解
//controller中不需要token验证的方法则用次注解 默认不许要验证
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
//controller中需要token验证的方法则用次注解 默认许要验证
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidateToken {
boolean required() default true;
}
方法预处理
//在次拦截 处理token
public class AuthenticationInterceptor implements HandlerInterceptor{
@Resource
private UserService userService;
//进入controller方法之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token=request.getHeader("token");//从http的请求头中取出
//如果不上方法则跳过
if (!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod) handler;
Method method=handlerMethod.getMethod();
//如果controller这个方法上面有这个 @PassToken 注解 返回 true 不需要token验证放行
if (method.isAnnotationPresent(PassToken.class)){
PassToken passToken=method.getAnnotation(PassToken.class);
if (passToken.required()){
return true;
}
}
//如果controller这个方法上面有这个 @ValidateToken 注解 返回 true token合格 否则token不合格教由全局异常处理
if (method.isAnnotationPresent(ValidateToken.class)){
ValidateToken validateToken=method.getAnnotation(ValidateToken.class);
if (validateToken.required()){
//开始token校验
if (token==null){
throw new RuntimeException("请求头中没有token");
}
//取出创建token时塞入的用户名
String username;
try {
username=JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException e) {
throw new RuntimeException("解析token异常");
}
User user=userService.findByUsername(username);
if (user==null){
throw new RuntimeException("当前用户不存在");
}
//验证token过期
JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("token已过期请重新登录");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
拦截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
// 拦截所有请求,通过判断是否有 @VilidateToken 注解 决定是否需要登录
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");
}
@Bean
public AuthenticationInterceptor authenticationInterceptor(){
return new AuthenticationInterceptor();
}
}
9.全局异常处理友好返回前端配置
//全局异常处理类 处理异常信息 友好地返回前台
@ControllerAdvice
public class GloabException {
@ResponseBody
@ExceptionHandler(Exception.class)
public String excetptionMsg(Exception e, HttpServletResponse response, HttpServletRequest request){
//解决跨域问题
response.setHeader("Access-Control-Allow-Origin","*");
//取出异常信息
String errorMsg=e.getMessage();
if (errorMsg!=null){
JSONObject jsonObject= new JSONObject();
jsonObject.put("errorMsg",errorMsg);
return jsonObject.toJSONString();
}
return null;
}
}
10.controller
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@Resource
private CreateTokenService createTokenService;
//用户登录
@RequestMapping(value = "/login",method = RequestMethod.POST)
public Object login(@RequestBody User user){
JSONObject jsonObject=new JSONObject();
User user1=userService.findByUsername(user.getUsername());
if (user1!=null){
if (user1.getPassword().equals(user.getPassword())){
String token=createTokenService.createToken(user);
jsonObject.put("token",token);
jsonObject.put("user",user1);
jsonObject.put("msg","登录成功");
return jsonObject;
}else {
jsonObject.put("errorMsg","用户密码不正确");
return jsonObject;
}
}else {
jsonObject.put("errorMsg","用户不存在");
return jsonObject;
}
}
//测试token是否生效
@ValidateToken//添加token验证 不加默认不验证
@RequestMapping(value = "/testToken",method = RequestMethod.GET)
public Object testToken(){
JSONObject jsonObject=new JSONObject();
jsonObject.put("msg","token配置生效");
return jsonObject;
}
}
10.启动项目 使用postman测试
登录
token验证
token有效时间内(我只给了它30s有效)
token已过期