还在直接用JWT做鉴权?JJWT真香

(给ImportNew加星标,提高Java技能)




jwt是什么?

JWTs是JSON对象的编码表示。JSON对象由零或多个名称/值对组成,其中名称为字符串,值为任意JSON值。JWT有助于在clear(例如在URL中)发送这样的信息,可以被信任为不可读(即加密的)、不可修改的(即签名)和URL - safe(即Base64编码的)。

jwt的组成
  • Header: 标题包含了令牌的元数据,并且在最小包含签名和/或加密算法的类型
  • Claims: Claims包含您想要签署的任何信息
  • JSON Web Signature (JWS): 在header中指定的使用该算法的数字签名和声明
  • 例如:

    Header:

    {
      "alg": "HS256",
      "typ": "JWT"
    }

    Claims:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }

    Signature:

    base64UrlEncode(Header) + "." + base64UrlEncode(Claims),

    加密生成的token:

    如何保证 JWT 安全

    有很多库可以帮助您创建和验证JWT,但是当使用JWT时,仍然可以做一些事情来限制您的安全风险。在您信任JWT中的任何信息之前,请始终验证签名。这应该是给定的。

    换句话说,如果您正在传递一个秘密签名密钥到验证签名的方法,并且签名算法被设置为“none”,那么它应该失败验证。

    确保签名的秘密签名,用于计算和验证签名。秘密签名密钥只能由发行者和消费者访问,不能在这两方之外访问。

    不要在JWT中包含任何敏感数据。这些令牌通常是用来防止操作(未加密)的,因此索赔中的数据可以很容易地解码和读取。

    如果您担心重播攻击,包括一个nonce(jti索赔)、过期时间(exp索赔)和创建时间(iat索赔)。这些在JWT规范中定义得很好。

    jwt的框架:JJWT

    JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。

  • JJWT的目标是最容易使用和理解用于在JVM上创建和验证JSON Web令牌(JWTs)的库。
  • JJWT是基于JWT、JWS、JWE、JWK和JWA RFC规范的Java实现。
  • JJWT还添加了一些不属于规范的便利扩展,比如JWT压缩和索赔强制。
  • 规范兼容:
  • 创建和解析明文压缩JWTs
  • 创建、解析和验证所有标准JWS算法的数字签名紧凑JWTs(又称JWSs):
  • HS256: HMAC using SHA-256
  • HS384: HMAC using SHA-384
  • HS512: HMAC using SHA-512
  • RS256: RSASSA-PKCS-v1_5 using SHA-256
  • RS384: RSASSA-PKCS-v1_5 using SHA-384
  • RS512: RSASSA-PKCS-v1_5 using SHA-512
  • PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
  • PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
  • PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
  • ES256: ECDSA using P-256 and SHA-256
  • ES384: ECDSA using P-384 and SHA-384
  • ES512: ECDSA using P-521 and SHA-512

  • 这里以github上的demo演示,理解原理,集成到自己项目中即可。 应用采用 spring boot + angular + jwt

    结构图

    Maven 引进 : pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.nibado.example</groupId>
        <artifactId>jwt-angular-spring</artifactId>
        <version>0.0.2-SNAPSHOT</version>
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>

            <commons.io.version>2.4</commons.io.version>
            <jjwt.version>0.6.0</jjwt.version>
            <junit.version>4.12</junit.version>
            <spring.boot.version>1.5.3.RELEASE</spring.boot.version>
        </properties>

        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring.boot.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${commons.io.version}</version>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jjwt.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
        </dependencies>
    </project>

    WebApplication.java

    package com.nibado.example.jwtangspr;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;

    @EnableAutoConfiguration
    @ComponentScan
    @Configuration
    public class WebApplication {
     
        //过滤器
        @Bean
        public FilterRegistrationBean jwtFilter() {
            final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            registrationBean.setFilter(new JwtFilter());
            registrationBean.addUrlPatterns("/api/*");
            return registrationBean;
        }

        public static void main(final String[] args) throws Exception {
            SpringApplication.run(WebApplication.class, args);
        }

    }

    JwtFilter.java

    package com.nibado.example.jwtangspr;

    import java.io.IOException;

    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;

    import org.springframework.web.filter.GenericFilterBean;

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureException;

    public class JwtFilter extends GenericFilterBean {

        @Override
        public void doFilter(final ServletRequest req,
                             final ServletResponse res,
                             final FilterChain chain) throws IOException, ServletException {
            final HttpServletRequest request = (HttpServletRequest) req;

            //客户端将token封装在请求头中,格式为(Bearer后加空格):Authorization:Bearer +token
            final String authHeader = request.getHeader("Authorization");
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                throw new ServletException("Missing or invalid Authorization header.");
            }

            //去除Bearer 后部分
            final String token = authHeader.substring(7);

            try {
             //解密token,拿到里面的对象claims
                final Claims claims = Jwts.parser().setSigningKey("secretkey")
                    .parseClaimsJws(token).getBody();
                //将对象传递给下一个请求
                request.setAttribute("claims", claims);
            }
            catch (final SignatureException e) {
                throw new ServletException("Invalid token.");
            }

            chain.doFilter(req, res);
        }

    }

    UserController.java

    package com.nibado.example.jwtangspr;

    import java.util.Arrays;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    import javax.servlet.ServletException;

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

    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;

    @RestController
    @RequestMapping("/user")
    public class UserController {

     //这里模拟数据库
        private final Map<String, List<String>> userDb = new HashMap<>();

        @SuppressWarnings("unused")
        private static class UserLogin {
            public String name;
            public String password;
        }
        
        public UserController() {
            userDb.put("tom", Arrays.asList("user"));
            userDb.put("wen", Arrays.asList("user", "admin"));
        }
        /*以上是模拟数据库,并往数据库插入tom和sally两条记录*/
        
        
        @RequestMapping(value = "login", method = RequestMethod.POST)
        public LoginResponse login(@RequestBody final UserLogin login)
            throws ServletException {
            if (login.name == null || !userDb.containsKey(login.name)) {
                throw new ServletException("Invalid login");
            }
            
            //加密生成token
            return new LoginResponse(Jwts.builder().setSubject(login.name)
                .claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "secretkey").compact());
        }

        @SuppressWarnings("unused")
        private static class LoginResponse {
            public String token;

            public LoginResponse(final String token) {
                this.token = token;
            }
        }
    }

    ApiController.java

    package com.nibado.example.jwtangspr;

    import io.jsonwebtoken.Claims;

    import java.util.List;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;

    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("/api")
    public class ApiController {
     @SuppressWarnings("unchecked")
     @RequestMapping(value = "role/{role}", method = RequestMethod.GET)
     public Boolean login(@PathVariable final String role,
       final HttpServletRequest request) throws ServletException {
      final Claims claims = (Claims) request.getAttribute("claims");
      return ((List<String>) claims.get("roles")).contains(role);
     }
    }

    index.html

    <!doctype html>
    <html ng-app="myApp">
    <head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>JSON Web Token / AngularJS / Spring Boot example</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="libs/bootstrap/css/bootstrap.css">
        <script src="libs/jquery/jquery.js"></script>
        <script src="libs/bootstrap/js/bootstrap.js"></script>
        <script src="libs/angular/angular.js"></script>
        <script src="app.js"></script>
    </head>
    <body>
    <div class="container" ng-controller='MainCtrl'>
     <h1>{{greeting}}</h1>
     <div ng-show="!loggedIn()">
      Please log in (tom and sally are valid names)</br>
      <form ng-submit="login()">
       Username: <input type="text" ng-model="userName"/><span><input type="submit" value="Login"/>
      </form>
     </div>
     <div class="alert alert-danger" role="alert" ng-show="error.data.message">
       <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
       <span class="sr-only">Error:</span>
       {{error.data.message}}
     </div> 
     <div ng-show="loggedIn()">
      <div class="row">
       <div class="col-md-6">
        <h3><span class="label label-success">Success!</span> Welcome {{userName}}</h3>
         <a href ng-click="logout()">(logout)</a>
       </div>
       <div class="col-md-4">
       <div class="row header">
       <div class="col-sm-4">{{userName}} is a</div>
      </div>
      <div class="row">
       <div class="col-sm-2">User</div>
       <div class="col-sm-2"><span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="roleUser"></span></div>
      </div>
      <div class="row">
       <div class="col-sm-2">Admin</div>
       <div class="col-sm-2"><span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="roleAdmin"></span></div>
      </div> 
      <div class="row">
       <div class="col-sm-2">Foo</div>
       <div class="col-sm-2"><span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="roleFoo"></span></div>
      </div>    
       </div>
      </div>
     </div>
    </div>
    </body>
    </html>

    app.js

    var appModule = angular.module('myApp', []);

    appModule.controller('MainCtrl', ['mainService','$scope','$http',
            function(mainService, $scope, $http) {
                $scope.greeting = 'Welcome to the JSON Web Token / AngularJR / Spring example!';
                $scope.token = null;
                $scope.error = null;
                $scope.roleUser = false;
                $scope.roleAdmin = false;
                $scope.roleFoo = false;

                $scope.login = function() {
                    $scope.error = null;
                    mainService.login($scope.userName).then(function(token) {
                        $scope.token = token;
                        $http.defaults.headers.common.Authorization = 'Bearer ' + token;
                        $scope.checkRoles();
                    },
                    function(error){
                        $scope.error = error
                        $scope.userName = '';
                    });
                }

                $scope.checkRoles = function() {
                    mainService.hasRole('user').then(function(user) {$scope.roleUser = user});
                    mainService.hasRole('admin').then(function(admin) {$scope.roleAdmin = admin});
                    mainService.hasRole('foo').then(function(foo) {$scope.roleFoo = foo});
                }

                $scope.logout = function() {
                    $scope.userName = '';
                    $scope.token = null;
                    $http.defaults.headers.common.Authorization = '';
                }

                $scope.loggedIn = function() {
                    return $scope.token !== null;
                }
            } ]);



    appModule.service('mainService', function($http) {
        return {
            login : function(username) {
                return $http.post('/user/login', {name: username}).then(function(response) {
                    return response.data.token;
                });
            },

            hasRole : function(role) {
                return $http.get('/api/role/' + role).then(function(response){
                    console.log(response);
                    return response.data;
                });
            }
        };
    });

    运行应用

    效果


     
     


    转自:csdn

    链接:https://blog.csdn.net/change_on/article/details/76279441


    - EOF -

    推荐阅读   点击标题可跳转

    1、这个 MySQL bug 99% 的人会踩坑!

    2、带颜色的JVM:三色标记详解

    3、Hbase 和 MySQL 的区别是什么?一文深度对比!


    看完本文有收获?请转发分享给更多人

    关注「ImportNew」,提升Java技能

    点赞和在看就是最大的支持❤️