spring boot 使用jwt做登陆验证

1.jwt是什么为啥要使用jwt

        什么是 JWT

2.融入spring boot 项目,编写认证思路

        2.1 用户登陆成功

        2.2 根据成功用户信息生成对应的jwt认证信息

        2.3 将认证信息返回给页面

        2.4 页面存认证信息

        2.5 其他请求在头部信息带上认证信息(这里可以约定放哪里,不一定在头部)

        2.6 后端统一在过滤器认证登陆信息

        2.7 认证信息解密后再次存起来好在后面具体业务使用

3.先引入jwt和fastjson的maven地址,fastjson在序列号的时候使用其他比较方便。

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

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

4.编写jwt的工具类JwtUtil和用于存中间转化的类UserRole,其中setExpiration 可以设置过期时间,到时候认证过的信息自动失效。

import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.ArrayList;
import java.util.Date;

/**
 * jwt 工具类 用户生成token 解密token
 */
public class JwtUtil {
    // step.1 获取密钥 可以配置
    private static final Key KEY = new SecretKeySpec("dkdi38393kd".getBytes(),
            SignatureAlgorithm.HS512.getJcaName());

    public static String getToken(UserRole userRole){

        // step 3 主体存储信息
        if(userRole.getRoles() == null){
            userRole.setRoles(new ArrayList<>());
        }
        // 为什么要转成拼接字符串 会比直接json 体积小一点点 。。。。。
        String payload  = userRole.getUserId() + ":" + userRole.getRoles();
        // step 4. 生成taken         setExpiration设置过期时候 到时间自动过期
        return "Bearer " + Jwts.builder().setExpiration(new Date(System.currentTimeMillis() + 1*24*60*60*100)).setPayload(payload).signWith(SignatureAlgorithm.HS512, KEY).compact();
    }

    public static String decryptTokenStr(String compactJws){
        compactJws = compactJws.replaceFirst("Bearer ","");
        String payload = Jwts.parser().setSigningKey(KEY).parse(compactJws).getBody().toString();
        System.out.println();
        return payload;
    }


    public static UserRole decryptToken(String payload){
        String[] split = payload.split(":");
        UserRole userRole = new UserRole();
        userRole.setUserId(Long.parseLong(split[0]));
        userRole.setRoles(JSON.parseArray(split[1], Long.class));
        return userRole;
    }

    // 通过 servlet 参数 获取登陆用户信息 不用总是解密
    public static UserRole decryptTokenByServlet(){
        HttpServletRequest request =((ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes()).getRequest();
        String userName = request.getHeader("userName");
        return decryptToken(userName);
    }
}
import java.util.List;

/**
 * 用户信息类,使用的时候比较方便
 */
public class UserRole {
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 角色id
     */
    private List<Long> roles;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public List<Long> getRoles() {
        return roles;
    }

    public void setRoles(List<Long> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "UserRole{" +
                "userId=" + userId +
                ", roles=" + roles +
                '}';
    }
}

5.编写登陆方法并且放回认证后的token。

import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RestController
public class UserController {

    @RequestMapping("user.login")
    public String login(@RequestBody UserLoginVo loginVo) {
        // 后期改为走数据库 校验 为了测试可以写死
        if("admin".equals(loginVo.getUserName()) && "admin".equals(loginVo.getPassWord())){
            UserRole userRole = new UserRole();
            userRole.setUserId(0L);
            userRole.setRoles(Arrays.asList(1L,2L));
            return JwtUtil.getToken(userRole);
        }
        return "";
    }

}

6.编写过滤器认证信息,这里的思路是先解密就解密后用户信息字符串存到servlet的head里面key值叫userName里面(可以自己取名字,获取的时候保持统一就行),在后续的业务代码不用再次解密,通过userName就可以直接转成用户信息类。

        使用过滤器记得在启动类加上该注解,basePackages的值为过滤器所在包

@ServletComponentScan(basePackages = "com.dh.test")
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebFilter(urlPatterns = "/*")
public class RequestFilter implements Filter {

    // 不校验的接口的集合 后期可以动态修改
    private static final List<String> noVerificationList = new ArrayList<>();
    static {
        noVerificationList.add("/user.login");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 这个变量 请求放到那个参数里面 就去那个参数里面的
        String authorization = request.getHeader("Authorization");

        String requestURI = request.getRequestURI();
        // 判断 接口是否是白名单
        if (noVerificationList.contains(requestURI)) {
            filterChain.doFilter(request, servletResponse);
            return;
        }
        if(StringUtils.isEmpty(authorization)){
            throw new RuntimeException("未登陆或登陆已经过期请重新登陆");
        }
        try{
            // 解密设置到 head里面是为了 防止后面多次解密
            RequestHeadWarp requestHeadWarp = new RequestHeadWarp(request);
            String payload = JwtUtil.decryptTokenStr(authorization);
            requestHeadWarp.addHead("userName",payload);
            // 这里得用 重写后的RequestHeadWarp
            filterChain.doFilter(requestHeadWarp, servletResponse);
        }catch (Exception e){
            throw new RuntimeException("未登陆或登陆已经过期请重新登陆");
        }
    }
}

8.因为servlet没有addHead的方法,所以重写HttpServletRequestWrapper的方法在过滤器doFilter的时候传入重写好后的类。

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class RequestHeadWarp extends HttpServletRequestWrapper {
    /**
     * 自定义的头部信息
     */
    private Map<String, String> customHeaders = new HashMap<>();
    /**
     * 自定义的头部key集合 在getHead 判断的时候可以不用将map转再判断
     */
    private Set<String> customHeaderNames = new HashSet<>();

    public RequestHeadWarp(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getHeader(String name) {
        if (customHeaderNames.contains(name)) {
            return customHeaders.get(name);
        }
        return super.getHeader(name);
    }

    public void addHead(String key,String value){
        customHeaders.put(key,value);
        customHeaderNames.add(key);
    }
}

9.编写测试接口看是否能正常解析token已经获取到用户信息。

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class TestController {

    @RequestMapping("test.get")
    public UserRole getUser(@RequestBody Map<String,String> map) {
        // 这里 只是测试 获取登陆后的用户信息,可以加入自己的业务代码
        return JwtUtil.decryptTokenByServlet();
    }
}

10.postman测试登陆接口。

11.postman测试其他接口,看是否能正常拦截未登陆和登陆

       11.1 先是未登陆,能看到正常抛出错误,提示未登陆。

        

        11.2 head 加入登陆认证的token后再次请求,能正常获取到登陆的用户信息。

       什么是token引入地址:什么是 JWT -- JSON WEB TOKEN - 简书

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值