Jwt在Java项目中的简单实际应用

1.什么是jwt

双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。

2.Jwt在javaweb项目中的简单使用

第一步:引入maven依赖

<!--引入JWT依赖,由于是基于Java,所以需要的是java-jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

第二步:创建两个注解,拦截器通过注释区分是否进行权限拦截(详情参考上一篇文章JWT在JAVAWEB项目中的应用核心步骤解读 https://www.cnblogs.com/jimisun/p/9480886.html)

package com.pjb.springbootjjwt.jimisun;

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 LoginToken {
    boolean required() default true;
}

package com.pjb.springbootjjwt.jimisun;

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 CheckToken {
    boolean required() default true;
}

第三步:编写JwtUtil工具类(生成token,解析token,校验token)

package com.pjb.springbootjjwt.jimisun;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @Author:jimisun
 * @Description:
 * @Date:Created in 14:08 2018/8/15
 * @Modified By:
 */
public class JwtUtil {

    /**
     * 用户登录成功后生成Jwt
     * 使用Hs256算法  私匙使用用户密码
     *
     * @param ttlMillis jwt过期时间
     * @param user      登录成功的user对象
     * @return
     */
    public static String createJWT(long ttlMillis, User user) {
        //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());
        claims.put("password", user.getPassword());

        //生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        String key = user.getPassword();

        //生成签发人
        String subject = user.getUsername();

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder,设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                //iat: jwt的签发时间
                .setIssuedAt(now)
                //代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
                .setSubject(subject)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, key);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * Token的解密
     * @param token 加密后的token
     * @param user  用户的对象
     * @return
     */
    public static Claims parseJWT(String token, User user) {
        //签名秘钥,和生成的签名的秘钥一模一样
        String key = user.getPassword();

        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //设置签名的秘钥
                .setSigningKey(key)
                //设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

    /**
     * 校验token
     * 在这里可以使用官方的校验,我这里校验的是token中携带的密码于数据库一致的话就校验通过
     * @param token
     * @param user
     * @return
     */
    public static Boolean isVerify(String token, User user) {
        //签名秘钥,和生成的签名的秘钥一模一样
        String key = user.getPassword();

        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //设置签名的秘钥
                .setSigningKey(key)
                //设置需要解析的jwt
                .parseClaimsJws(token).getBody();

        if (claims.get("password").equals(user.getPassword())) {
            return true;
        }

        return false;
    }

}

第四步:编写拦截器拦截请求进行权限验证

package com.pjb.springbootjjwt.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.pjb.springbootjjwt.jimisun.CheckToken;
import com.pjb.springbootjjwt.jimisun.JwtUtil;
import com.pjb.springbootjjwt.jimisun.LoginToken;
import com.pjb.springbootjjwt.jimisun.User;
import com.pjb.springbootjjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * jimisun
 */
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {

        // 从 http 请求头中取出 token
        String token = httpServletRequest.getHeader("token");
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有LoginToken注释,有则跳过认证
        if (method.isAnnotationPresent(LoginToken.class)) {
            LoginToken loginToken = method.getAnnotation(LoginToken.class);
            if (loginToken.required()) {
                return true;
            }
        }

        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(CheckToken.class)) {
            CheckToken checkToken = method.getAnnotation(CheckToken.class);
            if (checkToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getClaim("id").asString();
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("访问异常!");
                }
                User user = userService.findUserById(userId);
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                Boolean verify = JwtUtil.isVerify(token, user);
                if (!verify) {
                    throw new RuntimeException("非法访问!");
                }
                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 {

    }

}

配置拦截器(ps:我使用的是springboot,大家使用ssm配置拦截器的方式不一样)

package com.pjb.springbootjjwt.interceptorconfig;

import com.pjb.springbootjjwt.interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
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("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

第五步:在示例Controller中的实际应用

package com.pjb.springbootjjwt.jimisun;

import com.alibaba.fastjson.JSONObject;
import com.pjb.springbootjjwt.annotation.PassToken;
import com.pjb.springbootjjwt.annotation.UserLoginToken;
import com.pjb.springbootjjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.UUID;

/**
 * @Author:jimisun
 * @Description:
 * @Date:Created in 15:04 2018/8/15
 * @Modified By:
 */
@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserService userService;

    //登录
    @PostMapping("/login")
    @LoginToken
    public Object login(@RequestBody @Valid com.pjb.springbootjjwt.jimisun.User user) {

        JSONObject jsonObject = new JSONObject();
        com.pjb.springbootjjwt.jimisun.User userForBase = userService.findByUsername(user);
        if (userForBase == null) {
            jsonObject.put("message", "登录失败,用户不存在");
            return jsonObject;
        } else {
            if (!userForBase.getPassword().equals(user.getPassword())) {
                jsonObject.put("message", "登录失败,密码错误");
                return jsonObject;
            } else {
                String token = JwtUtil.createJWT(6000000, userForBase);
                jsonObject.put("token", token);
                jsonObject.put("user", userForBase);
                return jsonObject;
            }
        }
    }

    //查看个人信息
    @CheckToken
    @GetMapping("/getMessage")
    public String getMessage() {
        return "你已通过验证";
    }

}

最后一步,我们现在来访问一下啊

先进行登录

登录后携带token进行业务操作

简单的Demo程序就到这里了,当然还有很多东西没有考虑到,比如jwt在集群环境下的应用等一些问题留到下回探讨啦~

原文地址:https://www.cnblogs.com/niudaxianren/p/12596478.html

时间: 2024-08-30 10:47:59

Jwt在Java项目中的简单实际应用的相关文章

JAVA项目中公布WebService服务——简单实例

1.在Java项目中公布一个WebService服务: 怎样公布? --JDK1.6中JAX-WS规范定义了怎样公布一个WebService服务. (1)用jdk1.6.0_21以后的版本号公布. (2)与Web服务相关的类,都位于Javax.jws.*包中 @WebService--此注解用在类上指定将此类公布成一个WebService: EndPoint--此类为端点服务类,当中publish()方法用于将一个已经加入了@WebService注解对象绑定到一个地址的port上,用于公布. 2

JAVA项目中发布WebService服务——简单实例

1,在Java项目中发布一个WebService服务: 如何发布? --JDK1.6中JAX-WS规范定义了如何发布一个WebService服务: (1)用jdk1.6.0_21以后的版本发布: (2)与Web服务相关的类,都位于Javax.jws.*包中 @WebService--此注解用在类上指定将此类发布成一个WebService: EndPoint--此类为端点服务类,其中publish()方法用于将一个已经添加了@WebService注解对象绑定到一个地址的端口上,用于发布. 2,例子

ckeditor编辑器在java项目中配置

一.基本使用: 1.所需文件架包 A. Ckeditor基本文件包,比如:ckeditor_3.6.2.zip 下载地址:http://ckeditor.com/download 2.配置使用 A.将下载下来的CKEditor压缩解压,将解压后的文件夹("ckeditor")拷贝进项目里面,比如我是放在"WebContent"的"commons"文件夹下: B.在需要使用CKEditor的页面引入CKEditor的支持javascript <

java项目中可能会使用到的jar包解释

一.Struts2 用的版本是struts2.3.1.1 一个简单的Struts项目所需的jar包有如下8个 1. struts2-core-2.3.1.1.jar: Struts2的核心类库. 2. xwork-core-2.3.1.1.jar: XWork核心类,XWork是一个标准的command模式实现,并且完全从web层剥离出来.WebWork被构建在Xwork上,而Struts2由Struts1和WebWork两个经典的MVC框架发展而来. 3. ognl-3.0.3.jar: 支持

在MyEclipse的web项目/java项目中,使用Hibernate-tools中的hbm2java和hbm2ddl工具,根据hbm文件自动生成pojo和数据库脚本

     首先,我一定要吐槽下,这个Ant管理部署项目的工具,以及hibernate刚刚学习,导入我这一个简单的问题整了一天多,实在效率有点低下.在这两天中,①了解了Ant,知道了在Ant中很灵活的步骤项目的情况,知道了build.xml文件的一些基本写法.②还学习了在MyEclipse这样的集成工具中完成项目部署.③以及在这两种情况中,利用hibernateTools中的hbm2java和hbm2ddl工具,根据对象关系映射文件,自动生成POJO以及SQL文件(就是数据表). 一,在web项目

java项目中META-INF的作用中MANIFEST.MF的作用(转载)

发布Java应用程序时你会感到困难?好在Java提供了一系列打包和发布工具,可以显著的简化发布过程 该文章提供了打包Java code的几种方法,我们将会探讨Java manifest 文件,给出用于管理JAR文件所依赖文件.估计跨平台发布所需的CLasspath的合适方法.我也会解释如何使用manifest包版本特性来确认包的兼容性... 什么是JAR文件? 在开发过程中,我们可以直接使用Java class文件来运行程序,但这并不是一个好方式,好在Java 提供了 JAR(Java Arch

在MyEclipse的web项目/java项目中,使用Hibernate-tools中的hbm2java和hbm2ddl工具,依据hbm文件自己主动生成pojo和数据库脚本

     首先.我一定要吐槽下,这个Ant管理部署项目的工具.以及hibernate刚刚学习,导入我这一个简单的问题整了一天多.实在效率有点低下. 在这两天中,①了解了Ant.知道了在Ant中非常灵活的步骤项目的情况,知道了build.xml文件的一些基本写法.②还学习了在MyEclipse这种集成工具中完毕项目部署. ③以及在这两种情况中.利用hibernateTools中的hbm2java和hbm2ddl工具,依据对象关系映射文件,自己主动生成POJO以及SQL文件(就是数据表). 一,在w

Ant在Java项目中的使用(一眼就看会)

参考:http://www.cnblogs.com/zhengqiang/p/5557155.html Ant是跨平台的构建工具,它可以实现项目的自动构建和部署等功能.在本文中,主要让读者熟悉怎样将Ant应用到Java项目中,让它简化构建和部署操作. 一.安装与部署 1.1 下载 下载地址:https://archive.apache.org/dist/ant/binaries/ 我下载的是1.7.1版本.解压到某个目录(例如D:\ant-1.7.1),即可使用. 1.1 配置环境变量 1.添加

Java项目中的下载 与 上传

使用超级链接下载,一般会在浏览器中直接打开,而不是出现下载框 如果要确保出现下载框下载文件,则需要设置response中的参数: 1是要设置用附件的方式下载 Content-Disposition: attachment; filename = 2要设定内容的MIME类型 Content-Type:application/x-msdownload 下载的页面代码: <!DOCTYPE html> <html> <head> <meta charset="