模拟在SpringBoot项目中使用Jwt做登录认证

前面已经学习了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去访问
在这里插入图片描述

到这里,我们的模拟就结束了,当然如何在真正的项目中使用,请期待我下一期的哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值