下载demo项目: https://download.csdn.net/download/chw0629/21798838

1、用springboot脚手架构建项目
https://start.spring.io

 2、项目导入IDE

 3、导入相关依赖包包

		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.4.0</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>

		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>

4、创建实体类

R.java    //客户端请求返回对象

package com.yuer629.jwtdemo.entity;

import java.util.HashMap;
import java.util.Map;

/**
 * 返回数据
 */
public class R extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public R() {
		put("code", 0);
	}

	public static R error() {
		return error(500, "未知异常,请联系管理员");
	}

	public static R error(String msg) {
		return error(500, msg);
	}

	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static R ok(String msg) {
		R r = new R();
		r.put("msg", msg);
		return r;
	}

	public static R ok(Map<String, Object> map) {
		R r = new R();
		r.putAll(map);
		return r;
	}

	public static R ok() {
		return new R();
	}

	@Override
	public R put(String key, Object value) {
		super.put(key, value);
		return this;
	}
}

User.java    //用户对象

package com.yuer629.jwtdemo.api.jwt.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Objects;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    String Id;
    String username;
    String password;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return username.equals(user.username) &&
                password.equals(user.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(username, password);
    }
}

VirtualDB.java    //模拟数据库

package com.yuer629.jwtdemo.api.jwt.entity;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

/**
 * 模拟数据库 操作
 */
public class VirtualDB {

    //构造模拟数据
    private final static List<User> USER_LIST = Arrays.asList(
            new User[]{
                    new User("1","张三","zhangsan"),
                    new User("2","李四","lisi"),
                    new User("3","王五","ww123")
            }
    );


    /**
     * 根据用户名查询
     */
    public static User selectByUsername(String username){
        for(User u : USER_LIST){
            if(u.getUsername().equals(username)){
                return u;
            }
        }
        return null;
    }


    /**
     * 根据ID查询User
     * @param id
     */
    public static User selectById(Serializable id){
        for(User u : USER_LIST){
            if(u.getId().equals(id+"")){
                return u;
            }
        }
        return null;
    }



}

PassToken.java    //此注解用于绕开Token登陆验证

package com.yuer629.jwtdemo.api.jwt.entity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

UserLoginToken.java    //此注解用于校验Token登陆验证

package com.yuer629.jwtdemo.api.jwt.entity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
    boolean required() default true;
}

5、拦截器相关

AuthenticationInterceptor.java    //拦截器处理类

package com.yuer629.jwtdemo.api.jwt.Interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.yuer629.jwtdemo.api.jwt.entity.PassToken;
import com.yuer629.jwtdemo.api.jwt.entity.User;
import com.yuer629.jwtdemo.api.jwt.entity.UserLoginToken;
import com.yuer629.jwtdemo.api.jwt.entity.VirtualDB;
import com.yuer629.jwtdemo.exception.RRException;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Component
public class AuthenticationInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (StringUtils.isBlank(token)) {
                    throw new RRException("请在Header中携带token参数及相应的值");
                }
                // 获取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RRException("token解密失败,请检查token值");
                }
                //TODO 去数据库查询用户
                User user = VirtualDB.selectById(userId);

                if (user == null) {
                    throw new RRException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RRException("token验证失败,请重新获取");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
    }
}
InterceptorConfig.java   //拦截器配置类
package com.yuer629.jwtdemo.config;

import com.yuer629.jwtdemo.api.jwt.Interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @UserLoginToken 注解 决定是否需要登录
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

6、异常处理相关

RRException.java  // 自定义异常类
package com.yuer629.jwtdemo.exception;

/**
 * 自定义异常
 */
public class RRException extends RuntimeException {
	private static final long serialVersionUID = 1L;

	private String msg;
	private int code = 500;

	public RRException(String msg) {
		super(msg);
		this.msg = msg;
	}

	public RRException(String msg, Throwable e) {
		super(msg, e);
		this.msg = msg;
	}

	public RRException(String msg, int code) {
		super(msg);
		this.msg = msg;
		this.code = code;
	}

	public RRException(String msg, int code, Throwable e) {
		super(msg, e);
		this.msg = msg;
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

}
RRExceptionHandler.java    // 异常处理器(统一处理)
package com.yuer629.jwtdemo.exception;

import com.yuer629.jwtdemo.entity.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

/**
 * 异常处理器
 */
@RestControllerAdvice
public class RRExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 处理自定义异常
	 */
	@ExceptionHandler(RRException.class)
	public R handleRRException(RRException e) {
		R r = new R();
		r.put("code", e.getCode());
		r.put("msg", e.getMessage());

		return r;
	}

	@ExceptionHandler(DuplicateKeyException.class)
	public R handleDuplicateKeyException(DuplicateKeyException e) {
		logger.error(e.getMessage(), e);
		return R.error("数据库中已存在该记录");
	}

	@ExceptionHandler(Exception.class)
	public R handleException(Exception e) {
		logger.error(e.getMessage(), e);
		return R.error();
	}
}

7、TokenService.java

package com.yuer629.jwtdemo.api.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.yuer629.jwtdemo.api.jwt.entity.User;
import com.yuer629.jwtdemo.entity.R;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class TokenService {
    public static final long EXPIRATION_TIME = 2*60*60*1000L; // 过期时间:2 hour
    public R getToken(User user) {
        String token="";
        Long expirtesTime = System.currentTimeMillis() + EXPIRATION_TIME;
        token= JWT.create()
                .withAudience(user.getId())
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(expirtesTime))
                .withIssuedAt(new Date())
                .sign(Algorithm.HMAC256(user.getPassword()));
        return R.ok().put("exp",expirtesTime).put("token",token);
    }

}

8、jwt接口

JwtApi.java
package com.yuer629.jwtdemo.api.jwt;

import com.alibaba.fastjson.JSONObject;
import com.yuer629.jwtdemo.api.jwt.entity.User;
import com.yuer629.jwtdemo.api.jwt.entity.UserLoginToken;
import com.yuer629.jwtdemo.api.jwt.entity.VirtualDB;
import com.yuer629.jwtdemo.entity.R;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("jwt")
public class JwtApi {

    @Autowired
    TokenService tokenService;
    //登录
    @PostMapping("/login")
    public R login(@RequestBody User user){
        //检验参数的完整性
        if(StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())){
            return R.error("参数不完整");
        }
        JSONObject jsonObject=new JSONObject();

        //TODO 从数据库查询用户
        User userForBase= VirtualDB.selectByUsername(user.getUsername());
        
        if(userForBase==null){
            return R.error("登录失败,用户不存在");
        } else {
            if (!userForBase.getPassword().equals(user.getPassword())){
                //TODO 密码校验
                return R.error("登录失败,密码错误");
            } else {
                R r = tokenService.getToken(userForBase);
                return r.put("username",userForBase.getUsername());
            }
        }
    }

    @UserLoginToken
    @GetMapping("/getMessage")
    public R getMessage(){
        return R.ok("你已通过验证");
    }
}

9、配置文件允许SpringBean重写,在application.properties文件增加

spring.main.allow-bean-definition-overriding=true

10、postman 调用接口测试

curl: (可在命令行执行,或者粘贴到postman片段执行)

curl -X POST http://localhost:8080/jwt/login -H "Content-Type: application/json" -H "Postman-Token: 55bf274e-3e51-42b5-9d7a-1c9ec3550a0f" -H "cache-control: no-cache" -d "{\"username\":\"张三\",\"password\":\"zhangsan\"}"

登陆-正常

 登陆-密码错误:

登陆-用户名错误:

其他接口-正常:

 其他接口-无token:

 

 其他接口-token错误:

  其他接口-token过期:

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐