天天看點

JWT鑒權

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時,仍然可以做一些事情來限制您的安全風險。在您信任JWT中的任何資訊之前,請始終驗證簽名。這應該是給定的。

換句話說,如果您正在傳遞一個秘密簽名密鑰到驗證簽名的方法,并且簽名算法被設定為“none”,那麼它應該失敗驗證。

確定簽名的秘密簽名,用于計算和驗證簽名。秘密簽名密鑰隻能由發行者和消費者通路,不能在這兩方之外通路。

不要在JWT中包含任何敏感資料。這些令牌通常是用來防止操作(未加密)的,是以索賠中的資料可以很容易地解碼和讀取。

如果您擔心重播攻擊,包括一個nonce(jti索賠)、過期時間(exp索賠)和建立時間(iat索賠)。這些在JWT規範中定義得很好。

推薦:Java進階視訊資源

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

結構圖

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" target="_blank" rel="external nofollow" >
    <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;
            });
        }
    };
});
           

運作應用

JWT鑒權

效果

JWT鑒權
JWT鑒權

(感謝閱讀,希望對你所有幫助)

來源:blog.csdn.net/change_on/

article/details/76279441

JWT鑒權

Spring Boot 中的Http接口調用隻知道 RestTemplate?來試下 Retrofit !

JWT鑒權

Java中關于線程同步,你會用到的4個類

JWT鑒權

MySQL中使用“utf8”的坑有多少人遇到過,下次注意記得用“utf8mb4”

JWT鑒權

為啥微信有掃一掃登入,而我們沒有?從0到1搞定掃碼登入(附源碼)

JWT鑒權

Spring 官宣,要幹掉原生 JVM!

JWT鑒權

http協定無狀态中的 "狀态" 到底指的是什麼?!

JWT鑒權

沒有最好的架構,隻有更合适的架構。Redis低成本、高可用設計

JWT鑒權

繼續閱讀