Add JWT encoder to WebWolf
This commit is contained in:
parent
431da30946
commit
e78549fb72
@ -36,6 +36,11 @@
|
|||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.7.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package org.owasp.webwolf.jwt;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class JWTController {
|
||||||
|
|
||||||
|
@GetMapping("/WebWolf/jwt")
|
||||||
|
public ModelAndView jwt() {
|
||||||
|
return new ModelAndView("jwt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/WebWolf/jwt/decode", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public JWTToken decode(@RequestBody JWTToken token) {
|
||||||
|
token.decode();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/WebWolf/jwt/encode", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public JWTToken encode(@RequestBody JWTToken token) {
|
||||||
|
token.encode();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
86
webwolf/src/main/java/org/owasp/webwolf/jwt/JWTToken.java
Normal file
86
webwolf/src/main/java/org/owasp/webwolf/jwt/JWTToken.java
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package org.owasp.webwolf.jwt;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.jsonwebtoken.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class JWTToken {
|
||||||
|
|
||||||
|
private static final Pattern jwtPattern = Pattern.compile("(.*)\\.(.*)\\.(.*)");
|
||||||
|
|
||||||
|
private String encoded = "";
|
||||||
|
private String secretKey;
|
||||||
|
private String header;
|
||||||
|
private String payload;
|
||||||
|
private boolean signatureValid = true;
|
||||||
|
private boolean validToken = true;
|
||||||
|
|
||||||
|
public void decode() {
|
||||||
|
validToken = parseToken(encoded);
|
||||||
|
signatureValid = validateSignature(secretKey, encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode() {
|
||||||
|
var mapper = new ObjectMapper();
|
||||||
|
try {
|
||||||
|
if (StringUtils.hasText(secretKey)) {
|
||||||
|
encoded = Jwts.builder()
|
||||||
|
.signWith(SignatureAlgorithm.HS256, Base64Utils.encodeToUrlSafeString(secretKey.getBytes()))
|
||||||
|
.setClaims(mapper.readValue(payload, Map.class))
|
||||||
|
.setHeader(mapper.readValue(header, Map.class))
|
||||||
|
.compact();
|
||||||
|
} else {
|
||||||
|
encoded = Jwts.builder()
|
||||||
|
.setClaims(mapper.readValue(payload, Map.class))
|
||||||
|
.setHeader(mapper.readValue(header, Map.class))
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
validToken = true;
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
validToken = false;
|
||||||
|
signatureValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parseToken(String jwt) {
|
||||||
|
var matcher = jwtPattern.matcher(jwt);
|
||||||
|
var mapper = new ObjectMapper().writerWithDefaultPrettyPrinter();
|
||||||
|
if (matcher.matches()) {
|
||||||
|
try {
|
||||||
|
Jwt<Header, Claims> headerClaimsJwt = Jwts.parser().parseClaimsJwt(matcher.group(1) + "." + matcher.group(2) + ".");
|
||||||
|
this.header = mapper.writeValueAsString(headerClaimsJwt.getHeader());
|
||||||
|
this.payload = mapper.writeValueAsString(headerClaimsJwt.getBody());
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
this.header = mapper.writeValueAsString(new String(Base64Utils.decodeFromUrlSafeString(matcher.group(1))));
|
||||||
|
this.payload = mapper.writeValueAsString(new String(Base64Utils.decodeFromUrlSafeString(matcher.group(2))));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateSignature(String secretKey, String jwt) {
|
||||||
|
try {
|
||||||
|
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
79
webwolf/src/main/resources/static/js/jwt.js
Normal file
79
webwolf/src/main/resources/static/js/jwt.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
$(document).ready(() => {
|
||||||
|
$('#encodedToken').on('input', () => {
|
||||||
|
var token = $('#encodedToken').val();
|
||||||
|
var secretKey = $('#secretKey').val();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/WebWolf/jwt/decode',
|
||||||
|
data: JSON.stringify({encoded: token, secretKey: secretKey}),
|
||||||
|
success: function (data) {
|
||||||
|
if (data.validToken) {
|
||||||
|
$('#tokenHeader').val(data.header);
|
||||||
|
$('#tokenPayload').val(data.payload);
|
||||||
|
}
|
||||||
|
updateSignature(data);
|
||||||
|
},
|
||||||
|
contentType: "application/json",
|
||||||
|
dataType: 'json'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function encode() {
|
||||||
|
return () => {
|
||||||
|
var header = $('#tokenHeader').val();
|
||||||
|
var payload = $('#tokenPayload').val();
|
||||||
|
var secretKey = $('#secretKey').val();
|
||||||
|
var token = {header: header, payload: payload, secretKey: secretKey};
|
||||||
|
|
||||||
|
if (!parseJson(header)) {
|
||||||
|
$('#encodedToken').val("");
|
||||||
|
$('#tokenHeader').css('background-color', 'lightcoral');
|
||||||
|
} else if (!parseJson(payload)) {
|
||||||
|
$('#encodedToken').val("");
|
||||||
|
$('#tokenPayload').css('background-color', 'lightcoral');
|
||||||
|
} else {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: '/WebWolf/jwt/encode',
|
||||||
|
data: JSON.stringify(token),
|
||||||
|
success: function (data) {
|
||||||
|
$('#encodedToken').val(data.encoded);
|
||||||
|
$('#tokenPayload').css('background-color', '#FFFFFF');
|
||||||
|
$('#encodedToken').css('background-color', '#FFFFFF');
|
||||||
|
$('#tokenHeader').css('background-color', '#FFFFFF');
|
||||||
|
updateSignature(data);
|
||||||
|
},
|
||||||
|
contentType: "application/json",
|
||||||
|
dataType: 'json'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(() => {
|
||||||
|
$('#tokenPayload').on('input', encode());
|
||||||
|
$('#tokenHeader').on('input', encode());
|
||||||
|
$('#secretKey').on('input', encode());
|
||||||
|
});
|
||||||
|
|
||||||
|
function parseJson(text) {
|
||||||
|
try {
|
||||||
|
if (text) {
|
||||||
|
JSON.parse(text);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateSignature(data) {
|
||||||
|
if (data.signatureValid) {
|
||||||
|
$('#signatureValid').val("Signature valid");
|
||||||
|
} else {
|
||||||
|
$('#signatureValid').val("Signature invalid");
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,9 @@
|
|||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li><a th:href="@{/WebWolf/requests}">Incoming requests</a></li>
|
<li><a th:href="@{/WebWolf/requests}">Incoming requests</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li><a th:href="@{/WebWolf/jwt}">JWT</a></li>
|
||||||
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
|
||||||
<li><a href="#">
|
<li><a href="#">
|
||||||
|
65
webwolf/src/main/resources/templates/jwt.html
Normal file
65
webwolf/src/main/resources/templates/jwt.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<div th:replace="fragments/header :: header-css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div th:replace="fragments/header :: header"/>
|
||||||
|
|
||||||
|
<script type="text/javascript" th:src="@{/js/jwt.js}"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="alert alert-info fade in">
|
||||||
|
|
||||||
|
<a href="#" class="close" data-dismiss="alert">×</a>
|
||||||
|
<p>
|
||||||
|
Decode or encode a JWT some of the exercises need to encode or decode a new token
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="encodedToken">Encoded</label>
|
||||||
|
<form name="encodedTokenForm" id="encodedTokenForm" action="/WebWolf/jwt/encode" method="POST">
|
||||||
|
<textarea class="form-control" style="font-size: 14pt; font-family:monospace;" id="encodedToken" rows="4"
|
||||||
|
placeholder="Paste token here" spellcheck="false"></textarea>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Decoded</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-md-5">Header</div>
|
||||||
|
<div class="col-xs-6 col-md-7">Payload</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-md-5">
|
||||||
|
<textarea class="form-control" style="font-size: 14pt; font-family:monospace;" id="tokenHeader"
|
||||||
|
rows="12"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-6 col-md-7">
|
||||||
|
<textarea class="form-control" style="font-size: 14pt; font-family:monospace;" id="tokenPayload"
|
||||||
|
rows="12"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon" id="header">Secret key</span>
|
||||||
|
<input type="text" class="form-control" id="secretKey">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<h4 id="signatureValid">Invalid signature</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user