spring boot使用JWT登录验证

//参照链接:SpringBoot 集成token实践详解-CSDN博客

spring boot中登录的验证与授权

1.引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2.admin表

数据库创建admin表,要有账号(account)、密码(password)、角色(role)

创建实体类

@Data
public class Admin {
    private int adminId;
    private String account;
    private String password;
    private String role;
}
3.JwtUtil

Jwt具有产生token和破解token的作用。

加密:使用Jwt工具类将用户的账号、角色、时间封装起来,根据某种加密算法进行加密。

解密:对加密过后的信息使用DecodedJWT中的getClaim方法进行解密。

@Slf4j
@Component
public class JwtUtil {
    @Value("${token.privateKey}")
    private String privateKey;

    /**
     * 加密token.
     */
    public String getToken(String userId, String userRole) {
        log.info("输入参数 userId为 : "+userId);
        log.info("输入参数 userRole为 : "+userRole);
        //将用户信息、用户角色和创建时间封装加密
        String token = JWT
        .create()
        .withClaim("userId" ,userId)
        .withClaim("userRole",userRole)
        .withClaim("timeStamp", System.currentTimeMillis())
        .sign(Algorithm.HMAC256(privateKey));
        return token;
    }

    /**
     * 解析token.
     */
    public Map<String, String> parseToken(String token) {
        HashMap<String, String> map = new HashMap<>();
        DecodedJWT decodedjwt = JWT.require(Algorithm.HMAC256(privateKey))
        .build().verify(token);
        //对封装的用户信息进行解密
        Claim userId = decodedjwt.getClaim("userId");
        Claim userRole = decodedjwt.getClaim("userRole");
        Claim timeStamp = decodedjwt.getClaim("timeStamp");
        //将解密的用户信息放到map集合中,返回集合
        map.put("userId", userId.asString());
        map.put("userRole", userRole.asString());
        map.put("timeStamp", timeStamp.asLong().toString());
        return map;
    }
}

在application.yml中添加参数,设置加密方式、年轻token时间和老年token时间

token:
privateKey: 'fdasfgdsagaxgsregdfdjyghjfhebfdgwe45ygrfbsdfshfdsag'
yangToken: 1000000
oldToken: 3000000000
4.配置拦截路径

AuthWebMvcConfigurer实现WebMvcConfigurer接口;

addPathPatterns(需要拦截的路径)
excludePathPatterns(不需要拦截的路径)

/*
* 配置拦截路径
* */
@Configuration
public class AuthWebMvcConfigurer implements WebMvcConfigurer {
    @Autowired
    AuthHandlerInterceptor authHandlerInterceptor;

    /**
     * 给除了 /login 的接口都配置拦截器,拦截转向到 authHandlerInterceptor
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authHandlerInterceptor)
        .addPathPatterns("/**")
        .excludePathPatterns("/api/user/login");
    }
}

5.异常处理

TokenAuthExpiredException继承RuntimeException类

public class TokenAuthExpiredException extends RuntimeException{
}
6.拦截器

所有的接口请求均需要通过拦截器的拦截,除了配置拦截路径中excludePathPatterns("/api/user/login");不需要经过拦截器。

AuthHandlerInterceptor实现HandlerInterceptor接口;

@Slf4j
@Component
public class AuthHandlerInterceptor implements HandlerInterceptor {
    @Autowired
    JwtUtil jwtUtil;
    @Value("${token.privateKey}")
    private String privateKey;
    @Value("${token.yangToken}")
    private Long youngToken;
    @Value("${token.oldToken}")
    private Long oldToken;
    /**
     * 权限认证的拦截操作.
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        log.info("=======进入拦截器========");
        // 如果不是映射到方法直接通过,可以访问资源.
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        //为空就返回错误
        String token = httpServletRequest.getHeader("token");
        if (null == token || "".equals(token.trim())) {
            log.info("=========验证token失败,原因:请求的head没有token参数");
            return false;
        }
        log.info("=========验证====从请求的head中获取到token:" + token);
        Map<String, String> map = jwtUtil.parseToken(token);
        log.info("=========验证====解析token数据");
        String userId = map.get("userId");
        String userRole = map.get("userRole");
        log.info("=========验证====解析token数据 userId:"+userId);
        log.info("=========验证====解析token数据 userRole:"+userRole);
        long timeOfUse = System.currentTimeMillis() - Long.parseLong(map.get("timeStamp"));
        //1.判断 token 是否过期
        //年轻 token
        //token 距离发布token 2 个小时内的token为新生token
        if (timeOfUse < youngToken) {
            log.info("年轻 token");
            log.info("token时间失效");
            return true;
        }
            //老年 token 就刷新 token
        else if (timeOfUse >= youngToken && timeOfUse < oldToken) {
            log.info("token刷新");
            httpServletResponse.setHeader("token",jwtUtil.getToken(userId,userRole));
        }
            //过期 token 就返回 token 无效.
        else {
            throw new TokenAuthExpiredException();
        }
        log.info("判断当前登录的用户权限"+userRole+";");
        //2.角色匹配.
        if ("user".equals(userRole)) {
            log.info("========user账户============");
            return true;
        }
        if ("admin".equals(userRole)) {
            log.info("========admin账户============");
            return true;
        }
        return false;
    }

}

如果token过期, throw new TokenAuthExpiredException();

下面的代码是我暂未使用过的token过期,没用上

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 用户 token 过期
     * @return
     */
    @ExceptionHandler(value = TokenAuthExpiredException.class)
    @ResponseBody
    public String tokenExpiredExceptionHandler(){
        log.warn("用户 token 过期");
        return "用户 token 过期";
    }
}
7.测试

login模块查询数据库中的用户信息,进行加密,发放token

testToken模块就是模拟接口请求

@RestController
@Slf4j
@CrossOrigin
@RequestMapping("api/user")
public class AdminController {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private AdminService adminService;
    @PostMapping("/login")
    public String login(HttpServletRequest request, HttpServletResponse response){
        String account = request.getParameter("account");
        String password = request.getParameter("password");
        String role = request.getParameter("role");
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        wrapper.eq("account",account)
        .eq("role",role)
        .eq("password",password);
        Admin admin = adminService.getOne(wrapper);
        if (admin != null){
            return jwtUtil.getToken(account,role);
        }
        return "false";
    }

    @RequestMapping("/test-token")
    public Map<String,String> testToken(HttpServletRequest request){
        log.info("==拦截器执行完成后,进入此方法,开始======TestToken验证============");
        String token = request.getHeader("token");
        Map<String,String> vals= jwtUtil.parseToken(token);
        //String json=JSON.toJSONString(vals);
        //log.info("========json============"+json);
        log.info("==拦截器执行完成后,进入此方法,结束======TestToken验证============");
        return  vals;
    }
}

 输入账号密码角色,产生token

如果不带token是无法使用接口的

 

Header中携带token后即可使用接口

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值