前面已经学习了Jwt,现在来使用一下,在这个项目中框架采用的是springboot 测试软件是使用的postman,并没有连接数据库,只是做一个简单的模拟
1、搭建我们的框架结构
使用的Maven依赖如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!-- 热部署-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- lombook-->
<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>
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
<version>0.0.20131108.vaadin1</version>
<scope>compile</scope>
</dependency>
</dependencies>
然后配置我们的springBoot的配置文件 ,我使用的是yml格式
server:
port: 8080
spring:
application:
name: springboot-jwt
config:
jwt:
secret: abcdefg1234567
expire: 3600
header: token
2、编写Jwt工具类
package work.zx.Jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtil {
@Value("${config.jwt.secret}")
private String secret;
@Value("${config.jwt.expire}")
private long expire;
@Value("${config.jwt.header}")
private String header;
/**
* 生成token
*/
public String createToken(String subject){
//开始时间
Date nowDate=new Date();
//结束时间
Date expireDate = new Date(nowDate.getTime() + expire * 1000);
return Jwts.builder()
.setHeaderParam("typ","JWT")
.setSubject(subject)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512,secret)
.compact();
}
//获取token 中注册的信息 ,下面的方法会调用这个 拿到具体的信息
public Claims getTokenClaim(String token){
try{
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}catch (Exception e){
return null;
}
}
/**
* 获取用户名从token中
*/
public String getUsernameFromToken(String token) {
return getTokenClaim(token).getSubject();
}
//验证token是否过期
//传入的是token的失效时间
public boolean isTokenExpired (Date expirationTime) {
return expirationTime.before(new Date());
}
//获取token的失效时间
public Date getExpirationDateFromToken(String token) {
return getTokenClaim(token).getExpiration();
}
//获取发布时间
public Date getIssuedAtDateFromToken(String token) {
return getTokenClaim(token).getIssuedAt();
}
//get and set
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpire() {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
}
3、编写统一json数据返回类
package work.zx.pojo;
/**
* 返回类
*/
public enum ResultCode {
SUCCESS(true,10000,"操作成功!"),
//---系统错误返回码-----
FAIL(false,10001,"操作失败"),
UNAUTHENTICATED(false,10002,"您还未登录"),
UNAUTHORISE(false,10003,"权限不足"),
SERVER_ERROR(false,99999,"抱歉,系统繁忙,请稍后重试!"),
//---用户操作返回码 2xxxx----
MOBILEORPASSWORDERROR(false,20001,"用户名或密码错误");
boolean success;
int code;
String message;
ResultCode(boolean success,int code, String message){
this.success = success;
this.code = code;
this.message = message;
}
public boolean success() {
return success;
}
public int code() {
return code;
}
public String message() {
return message;
}
}
4、编写全局异常处理类
package work.zx.error;
import io.jsonwebtoken.SignatureException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import work.zx.pojo.ResultCode;
@RestControllerAdvice
public class PermissionHandler {
@ExceptionHandler(value = { SignatureException.class })
@ResponseBody
public ResultCode authorizationException(SignatureException e){
return ResultCode.FAIL;
}
}
5、编写Controller类
package work.zx.Controller;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import work.zx.Jwt.JwtUtil;
import work.zx.pojo.ResultCode;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
public class TokenController {
@Resource
private JwtUtil jwtConfig ;
/**
* 用户登录接口
* @param userName
* @param passWord
* @return
*/
@PostMapping("/login")
public String login (@RequestParam("userName") String userName,
@RequestParam("passWord") String passWord) throws JSONException {
JSONObject json = new JSONObject();
/** 验证userName,passWord和数据库中是否一致,如不一致,直接return ResultTool.errer(); 【这里省略该步骤】*/
// 这里模拟通过用户名和密码,从数据库查询userId
// 这里把userId转为String类型,实际开发中如果subject需要存userId,则可以JwtConfig的createToken方法的参数设置为Long类型
String userId = 5 + "";
String token = jwtConfig.createToken(userId) ;
if (!StringUtils.isEmpty(token)) {
json.put("token",token) ;
}
return json.toString();
}
/**
* 需要 Token 验证的接口 也就是必须携带token才能正常访问
*/
@PostMapping("/info")
public String info (){
return "info";
}
/**
* 根据请求头的token获取userId
* @param request
* @return
*/
@GetMapping("/getUserInfo")
public String getUserInfo(HttpServletRequest request){
String usernameFromToken = jwtConfig.getUsernameFromToken(request.getHeader("token"));
System.out.println(usernameFromToken);
return usernameFromToken;
}
/*
为什么项目重启后,带着之前的token还可以访问到需要info等需要token验证的接口?
答案:只要不过期,会一直存在,类似于redis,
*/
}
6、编写我们自己的处理程序拦截适配器
package work.zx.config;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import work.zx.Jwt.JwtUtil;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
@Resource
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//地址过滤
String url=request.getRequestURL().toString();
if(url.contains("/login")){
//登录地址直接放过
return true;
}
/** Token 验证 */
String token = request.getHeader(jwtUtil.getHeader());
if(StringUtils.isEmpty(token)){
token = request.getParameter(jwtUtil.getHeader());
}
//判断非空
if(StringUtils.isEmpty(token)){
throw new SignatureException(jwtUtil.getHeader()+ "不能为空");
}
Claims claims = null;
try{
//失效的情况
claims=jwtUtil.getTokenClaim(token);
if(claims==null || jwtUtil.isTokenExpired(claims.getExpiration())){
throw new SignatureException(jwtUtil.getHeader()+"失效了");
}
}catch ( Exception e){
e.printStackTrace();
throw new SignatureException(jwtUtil.getHeader()+"失效了");
}
/** 设置 identityId 用户身份ID */
request.setAttribute("identityId", claims.getSubject());
return true;
}
}
7、将我们上面编写的处理器配置到拦截器中
package work.zx.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private TokenInterceptor tokenInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
}
}
8、最后我们开始测试
1、访问登录,获取到token
2、尝试不携带token去访问info
3、携带token去访问
到这里,我们的模拟就结束了,当然如何在真正的项目中使用,请期待我下一期的哦