1. TOOLS AND ENV
IDE : Spring Tool Suite 3.7.3
JDK : 1.8 Tomcat : 8.0.18
Spring : 4.2.6.RELEASE
2. OAUTH2 FLOW
Basically , resources are protected with access token, client could use refresh token to request an access token if authenticated with its credentials.
3. POM.XML
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<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/maven-v4_0_0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>org.junjun.spring.tutorials</groupId> | |
<artifactId>spring-mvc-oauth2</artifactId> | |
<name>spring-4-mvc-sample</name> | |
<packaging>war</packaging> | |
<version>1.0.0</version> | |
<properties> | |
<java.version>1.8</java.version> | |
<org.springframework.version>4.2.6.RELEASE</org.springframework.version> | |
<org.spring.security.version>4.1.0.RELEASE</org.spring.security.version> | |
<slf4j.version>1.7.10</slf4j.version> | |
</properties> | |
<dependencies> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-context</artifactId> | |
<version>${org.springframework.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-webmvc</artifactId> | |
<version>${org.springframework.version}</version> | |
</dependency> | |
<!-- Spring JAXB support --> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-oxm</artifactId> | |
<version>${org.springframework.version}</version> | |
</dependency> | |
<!-- Spring Security Web --> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-web</artifactId> | |
<version>${org.spring.security.version}</version> | |
</dependency> | |
<!-- Spring Security Config --> | |
<dependency> | |
<groupId>org.springframework.security</groupId> | |
<artifactId>spring-security-config</artifactId> | |
<version>${org.spring.security.version}</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.security.oauth</groupId> | |
<artifactId>spring-security-oauth2</artifactId> | |
<version>2.0.10.RELEASE</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.validation</groupId> | |
<artifactId>validation-api</artifactId> | |
<version>1.1.0.Final</version> | |
</dependency> | |
<dependency> | |
<groupId>org.hibernate</groupId> | |
<artifactId>hibernate-validator</artifactId> | |
<version>5.1.2.Final</version> | |
</dependency> | |
<dependency> | |
<groupId>org.slf4j</groupId> | |
<artifactId>slf4j-log4j12</artifactId> | |
<version>1.7.5</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>javax.servlet-api</artifactId> | |
<version>3.1.0</version> | |
<scope>provided</scope> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet.jsp</groupId> | |
<artifactId>javax.servlet.jsp-api</artifactId> | |
<version>2.3.1</version> | |
<scope>provided</scope> | |
</dependency> | |
<dependency> | |
<groupId>javax.servlet</groupId> | |
<artifactId>jstl</artifactId> | |
<version>1.2</version> | |
</dependency> | |
<dependency> | |
<groupId>com.fasterxml.jackson.core</groupId> | |
<artifactId>jackson-databind</artifactId> | |
<version>2.7.4</version> | |
</dependency> | |
</dependencies> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-war-plugin</artifactId> | |
<configuration> | |
<failOnMissingWebXml>false</failOnMissingWebXml> | |
</configuration> | |
</plugin> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<configuration> | |
<source>${java.version}</source> | |
<target>${java.version}</target> | |
<compilerArgument>-Xlint:all</compilerArgument> | |
<showWarnings>true</showWarnings> | |
<showDeprecation>true</showDeprecation> | |
</configuration> | |
</plugin> | |
</plugins> | |
</build> | |
</project> |
spring-security-oauth2 added to the dependency.
4. OAUTH2 CONFIG
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.junjun.spring.tutorials.config; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.beans.factory.annotation.Qualifier; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.http.HttpMethod; | |
import org.springframework.security.authentication.AuthenticationManager; | |
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; | |
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; | |
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; | |
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; | |
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; | |
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; | |
@Configuration | |
public class OAuth2Config { | |
@Configuration | |
@EnableAuthorizationServer | |
protected static class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter { | |
@Autowired | |
@Qualifier("authenticationManagerBean") | |
private AuthenticationManager authenticationManager; | |
@Override | |
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { | |
endpoints.authenticationManager(authenticationManager); | |
} | |
@Override | |
public void configure(ClientDetailsServiceConfigurer clients) throws Exception { | |
clients.inMemory().withClient("springuser").secret("springpassword") | |
.authorizedGrantTypes("authorization_code", "refresh_token", "password") | |
.scopes("read", "write", "trust"); | |
} | |
} | |
@Configuration | |
@EnableResourceServer | |
protected static class ResourceServer extends ResourceServerConfigurerAdapter { | |
@Override | |
public void configure(HttpSecurity http) throws Exception { | |
http.requestMatchers().antMatchers(HttpMethod.GET, "/api/**").and().authorizeRequests().anyRequest() | |
.access("#oauth2.hasScope('read')"); | |
http.requestMatchers().antMatchers(HttpMethod.POST, "/api/**").and().authorizeRequests().anyRequest() | |
.access("#oauth2.hasScope('write')"); | |
} | |
} | |
} |
1. Technically authorization server and resource server can be hosted separately.
2. OAuth2 authentication manager is define in Spring Web Security in class SecurityConfig
3. You may refer to Spring documents for all possible values and use cases for grant types and scopes
5. JS AND JSP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
$(document).ready(function() { | |
// var token = $("meta[name='_csrf']").attr("content"); | |
// var header = $("meta[name='_csrf_header']").attr("content"); | |
// | |
// $(document).ajaxSend(function(e, xhr, options) { | |
// xhr.setRequestHeader(header, token); | |
// }); | |
$.fn.serializeObject = function() | |
{ | |
var o = {}; | |
var a = this.serializeArray(); | |
$.each(a, function() { | |
if (o[this.name] !== undefined) { | |
if (!o[this.name].push) { | |
o[this.name] = [o[this.name]]; | |
} | |
o[this.name].push(this.value || ""); | |
} else { | |
o[this.name] = this.value || ""; | |
} | |
}); | |
return o; | |
}; | |
$(document).on("#form-todo").submit(function(event) { | |
event.preventDefault(); | |
return false; | |
}); | |
$(document).on("click", "#link-logout", function(event) { | |
event.preventDefault() | |
$("#form-logout")[0].submit(); | |
}); | |
$(document).on("click", "#rest-create-btn", function() { | |
var form = $("#form-token"); | |
var url = form.attr("action"); | |
var token = ""; | |
$.ajax({ | |
type: "POST", | |
url: url, | |
data: form.serialize(), | |
beforeSend: function(xhr) { | |
xhr.setRequestHeader("Authorization", "Basic c3ByaW5ndXNlcjpzcHJpbmdwYXNzd29yZA==" ) | |
// base64 username:password | |
}, | |
success: function(response) | |
{ | |
if(response.access_token){ | |
token = response.access_token; | |
form = $("#form-todo"); | |
url = $("#rest-url-create").val(); | |
var data = JSON.stringify(form.serialize()); | |
data = JSON.stringify(form.serializeObject()); | |
$.ajax({ | |
type : "POST", | |
url : url, | |
headers: { | |
"Authorization":"Bearer "+token | |
}, | |
data : data, | |
contentType: "application/json; charset=utf-8", | |
dataType: "json", | |
success : function(response) { | |
var output="<li>" + response.date + " : " +response.content + "</li>"; | |
$("#ul-todo-list").append(output); | |
$("#todoContent").val(""); | |
} | |
}); | |
} | |
} | |
}); | |
}); | |
}); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ page language="java" contentType="text/html; charset=UTF-8" | |
pageEncoding="UTF-8"%> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> | |
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> | |
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<meta name="description" content="spring"> | |
<meta name="keywords" content="spring"> | |
<meta name="_csrf" content="${_csrf.token}" /> | |
<meta name="_csrf_header" content="${_csrf.headerName}" /> | |
<title></title> | |
<link rel="stylesheet" | |
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> | |
</head> | |
<body> | |
<header class="navbar navbar-default navbar-fixed-top bs-docs-nav" | |
role="banner"> | |
<div class="container"> | |
<nav class="collapse navbar-collapse bs-navbar-collapse"> | |
<ul class="nav navbar-nav navbar-right"> | |
<li><a id="link-logout" href="javascript:;"> Logout </a></li> | |
</ul> | |
</nav> | |
</div> | |
</header> | |
<div class="container" style="padding-top: 10%"> | |
<div class="row"> | |
<form:form id="form-todo" class="form-horizontal" | |
modelAttribute="newToDo" action=""> | |
<input name="${_csrf.parameterName}" type="hidden" | |
value="${_csrf.token}"> | |
<div class="form-group"> | |
<div class="col-md-offset-2 col-md-6"> | |
<form:input id="todoContent" class="form-control" path="content" | |
placeholder="" /> | |
</div> | |
<div class="col-md-2"> | |
<button id="rest-create-btn" class="btn btn-success btn-block">Create</button> | |
</div> | |
<label for="todoContent" class="col-md-4 control-label" | |
style="text-align: left;"><spring:bind path="content"> | |
<c:if test="${status.error}"> | |
<form:errors path="content" class="text-danger" /> | |
</c:if> | |
</spring:bind></label> | |
</div> | |
</form:form> | |
</div> | |
<div class="row"> | |
<div class="col-md-offset-2 col-md-10"> | |
<ul id="ul-todo-list"> | |
<c:forEach items="${toDoList}" var="toDo"> | |
<li>${toDo.createdDateDisplay} : ${toDo.content}</li> | |
</c:forEach> | |
</ul> | |
</div> | |
</div> | |
<input type="hidden" id="rest-url-create" | |
value="${pageContext.request.contextPath}/api/todo/create" /> <input | |
type="hidden" id="rest-url-create" | |
value="${pageContext.request.contextPath}/api/todo/create" /> | |
</div> | |
<form id="form-token" | |
action="${pageContext.request.contextPath}/oauth/token" method="post"> | |
<input name="grant_type" type="hidden" value="password" /> <input | |
name="username" type="hidden" value="springuser" /> <input | |
name="password" type="hidden" value="springpassword" /> | |
</form> | |
<form id="form-logout" | |
action="${pageContext.request.contextPath}/logout" method="post"> | |
<input name="${_csrf.parameterName}" type="hidden" | |
value="${_csrf.token}"> | |
</form> | |
<script src="http://code.jquery.com/jquery-latest.min.js"></script> | |
<script | |
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> | |
<script src="${pageContext.request.contextPath}/js/todo/rest/list.js"></script> | |
</body> | |
</html> |
1. Get OAUTH2 token before consuming api restful web service
2. csrf is disabled just to make example simple
6. TEST
7. SOURCE CODE
https://github.com/junjun-dachi/spring-tutorials/tree/master/05-spring-mvc-oauth2
8. Reference
https://spring.io/guides/tutorials/spring-boot-oauth2/
https://github.com/spring-projects/spring-security-oauth
https://github.com/spring-projects/spring-security-oauth/blob/master/docs/oauth2.md
https://raymondhlee.wordpress.com/2014/12/21/implementing-oauth2-with-spring-security/
Anyone can take the 5-minute java test in the US as it is targeted to coders who want to test their Java knowledge.
ReplyDelete