Json web token(JWT)简介&&入门 登录demo

JWT介绍:
    JWT是一个开放的标准,用于在各方之间以json对象安全的传输信息,这些信息可以通过数字签名进行验证和信任。
    
    JWT请求流程:1. 用户使用账号和面发出post请求;
                            2. 服务器使用私钥创建一个jwt;
                            3. 服务器返回这个jwt给浏览器;
                            4. 浏览器将该jwt串在请求头中向服务器发送请求;
                            5. 服务器验证该jwt;
                            6. 返回响应的资源给浏览器。

    JWT的组成:jwt含有三部分:头部(header)、载荷(payload)、签证(signature)
    头部:头部一般有两部分信息:声明类型、声明加密的算法(通常使用HMAC SHA256),头部一般使用base64加密:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    载荷:该部分一般存放一些有效的信息,jwt的标准定义包含五个字段:
            - iss:该JWT的签发者
            - sub: 该JWT所面向的用户
            - aud: 接收该JWT的一方
            - exp(expires): 什么时候过期,这里是一个Unix时间戳
            - iat(issued at): 在什么时候签发的
            这个只是JWT的定义标准,不强制使用。另外自己也可以添加一些公开的不涉及安全的方面的信息    
       签证:该部分是使用了HS256加密后的数据;包含三个部分:
            - header (base64后的)
            - payload (base64后的)
            - secret 私钥

            secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。


JWT在spring boot中的使用:
    首先要添加JWT的依赖:
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>
    然后在配置文件中添加JWT的配置信息:
        audience:
        clientId: 098f6bcd4621d373cade4e832627b4f6
        base64Secret: MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=
        name: restapiuser
        expiresSecond: 172800
    配置信息的实体类,以便获取jwt的配置:在config下面
    @Data
    @ConfigurationProperties(prefix = "audience")
    @Component
    public class Audience {
    private String clientId;
    private String base64Secret;
    private String name;
    private int expiresSecond;
    }
        
    添加拦截器:
    public class JwtFilter extends GenericFilterBean {

    @Autowired
    private Audience audience;

    /**
     *  Reserved claims(保留),它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定好的claim有:

     iss(Issuser):代表这个JWT的签发主体;
     sub(Subject):代表这个JWT的主体,即它的所有人;
     aud(Audience):代表这个JWT的接收对象;
     exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
     nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
     iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
     jti(JWT ID):是JWT的唯一标识。
     * @param req
     * @param res
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
            throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        //等到请求头信息authorization信息
        final String authHeader = request.getHeader("authorization");

        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            chain.doFilter(req, res);
        } else {

            if (authHeader == null || !authHeader.startsWith("bearer;")) {
                throw new LoginException(ResultEnum.LOGIN_ERROR);
            }
            final String token = authHeader.substring(7);

            try {
                if(audience == null){
                    BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
                    audience = (Audience) factory.getBean("audience");
                }
                final Claims claims = JwtHelper.parseJWT(token,audience.getBase64Secret());
                if(claims == null){
                    throw new LoginException(ResultEnum.LOGIN_ERROR);
                }
                request.setAttribute(Constants.CLAIMS, claims);
            } catch (final Exception e) {
                throw new LoginException(ResultEnum.LOGIN_ERROR);
            }

            chain.doFilter(req, res);
            }
        }
    }
    
    注册JWT拦截器,可以在配置类中,也可以直接在SpringBoot的入口类中:   

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        source.registerCorsConfiguration("/**", config);
        final FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new JwtFilter());
//        bean.setOrder(0);
        bean.addUrlPatterns("/secure/*");
        return bean;

    }

 编写登录Controller:

@RestController
@RequestMapping("/user")
public class LoginController {
    
//    @Autowired
//    private UserService userService;
    
    @ApiOperation(value="用户登录", notes="使用JWT实现用户登录demo")
    @GetMapping("/login")
    public Object login(String userName,String passpword) throws ServletException{
        
         if (userName == "" || userName == null || passpword == "" || passpword == null)

                throw new ServletException("Please fill in username and password");

        //这里不仅要判断前台传来的用户名和密码是否为空,而且要到数据库校验,判断是否注册过此用户,这里省略

        //创建 JWT token
         String jwtToken = Jwts.builder().setSubject(userName).claim("roles", "member").setIssuedAt(new Date())
                    .signWith(SignatureAlgorithm.HS256, "secretkey").compact();

         return jwtToken;
    }

}

我们调用loginController中的login方法会返回一个token,值为:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsaWp1YW4iLCJyb2xlcyI6Im1lbWJlciIsImlhdCI6MTUyNzU1NzU2MH0.TcthyjCcJCR4ciD5F-Ps_3WbLmgpN8l1t-3rewv05TA

这就是JWT登录成功后返回给客户端的秘钥token


我们再新建一个SecureController判断是否登录成功,

@RestController
@RequestMapping("/secure")
public class SecureController {

    @RequestMapping("/users/user")
    public String loginSuccess() {
        return "Login Successful!";
    }

}

使用postman调用SecureController中的 loginSuccess方法时,会报错:

{   "timestamp": 1527560046368,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "javax.servlet.ServletException",
    "message": "Missing or invalid Authorization header",
    "path": "/secure/users/user"

}

这是因为请求此接口的时候,没有带上登录成功后生成的token值

我们在Authorization中选择TYPE为Breaer Token ,并将登录返回给客户端的token放在此Token中,再请求此验证是否登录成功的接口,此时会返回:"Login Successful!"

至此,JWT入门demo已结束。








猜你喜欢

转载自blog.csdn.net/weixin_39702323/article/details/80492230
今日推荐