Add REST sample

Fixes gh-89
This commit is contained in:
Rob Winch
2014-12-23 16:36:00 -06:00
parent ddff1f4c0d
commit 59dbd75ca2
13 changed files with 422 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ commonsPoolVersion=2.2
embeddedRedisVersion=0.2
gebVersion=0.10.0
groovyVersion=2.3.8
jacksonVersion=2.4.4
jedisVersion=2.4.1
jstlVersion=1.2.1
seleniumVersion=2.44.0

24
samples/rest/build.gradle Normal file
View File

@@ -0,0 +1,24 @@
apply from: JAVA_GRADLE
apply from: TOMCAT_GRADLE
tasks.findByPath("artifactoryPublish")?.enabled = false
sonarRunner {
skipProject = true
}
dependencies {
compile project(':spring-session-data-redis'),
"org.springframework:spring-webmvc:$springVersion",
"redis.embedded:embedded-redis:0.2",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
jstlDependencies
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
testCompile 'junit:junit:4.11'
integrationTestCompile spockDependencies,
'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
}

View File

@@ -0,0 +1,70 @@
package sample
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise
import javax.servlet.http.HttpServletResponse
/**
* Ensures that Spring Security and Session are working
*
* @author Rob Winch
*/
@Stepwise
class RestTests extends Specification {
@Shared
RESTClient client = new RESTClient(System.properties.'geb.build.baseUrl')
@Shared
String session
def 'Unauthenticated user sent to log in page'() {
when: 'unauthenticated user request protected page'
def resp = client.get path: '/', headers: ['Accept':'application/json']
then: 'sent to the log in page'
def e = thrown(HttpResponseException)
e.response.status == HttpServletResponse.SC_UNAUTHORIZED
}
def 'Authenticate with Basic Works'() {
when: 'Authenticate with Basic'
def username, response
client.get(path: '/', headers: ['Authorization': 'Basic ' + 'user:password'.bytes.encodeBase64() ]) { resp, json ->
response = resp
username = json.username
session = resp.headers.'x-auth-token'
}
then: 'Access the User information and obtain session via x-auth-token header'
response.status == HttpServletResponse.SC_OK
username == 'user'
session
}
def 'Authenticate with x-auth-token works'() {
when: 'Authenticate with x-auth-token'
def username, response
client.get(path: '/', headers: ['x-auth-token': session ]) { resp, json ->
response = resp
username = json.username
}
then: 'Access the User information'
response.status == HttpServletResponse.SC_OK
username == 'user'
}
def 'Logout'() {
when: 'invalide session'
def response
client.get(path: '/logout', headers: ['x-auth-token': session ]) { resp, json ->
response = resp
session = resp.headers.'x-auth-token'
}
then: 'The session is deleted and an empty x-auth-token is returned'
response.status == HttpServletResponse.SC_NO_CONTENT
session == ''
}
}

View File

@@ -0,0 +1,70 @@
package sample;
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Protocol;
import redis.embedded.RedisServer;
/**
* Runs an embedded Redis instance. This is only necessary since we do not want
* users to have to setup a Redis instance. In a production environment, this
* would not be used since a Redis Server would be setup.
*
* @author Rob Winch
*/
@Configuration
public class EmbeddedRedisConfiguration {
@Bean
public static RedisServerBean redisServer() {
return new RedisServerBean();
}
/**
* Implements BeanDefinitionRegistryPostProcessor to ensure this Bean
* is initialized before any other Beans. Specifically, we want to ensure
* that the Redis Server is started before RedisHttpSessionConfiguration
* attempts to enable Keyspace notifications.
*/
static class RedisServerBean implements InitializingBean, DisposableBean, BeanDefinitionRegistryPostProcessor {
private RedisServer redisServer;
public void afterPropertiesSet() throws Exception {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
}
public void destroy() throws Exception {
if(redisServer != null) {
redisServer.stop();
}
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.HeaderHttpSessionStrategy;
import org.springframework.session.web.http.HttpSessionStrategy;
/**
* @author Rob Winch
*/
@Configuration
@Import(EmbeddedRedisConfiguration.class) // <1>
@EnableRedisHttpSession // <2>
public class HttpSessionConfig {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <3>
}
@Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy(); // <4>
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
/**
* @author Rob Winch
*/
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample;
/**
* @author Rob Winch
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* @author Rob Winch
*/
public class SecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample.mvc;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* @author Rob Winch
*/
@Configuration
@EnableWebMvc
@ComponentScan
public class MvcConfig {
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample.mvc;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import sample.HttpSessionConfig;
import sample.SecurityConfig;
/**
* @author Rob Winch
*/
public class MvcInializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class, HttpSessionConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { MvcConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package sample.mvc;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
/**
* @author Rob Winch
*/
@RestController
public class RestDemoController {
@RequestMapping(value="/",produces = "application/json")
public Map<String,String> helloUser(Principal principal) {
HashMap<String,String> result = new HashMap<String,String>();
result.put("username", principal.getName());
return result;
}
@RequestMapping("/logout")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void logout(HttpSession session) {
session.invalidate();
}
}

View File

@@ -5,6 +5,7 @@ include 'docs'
include 'samples:boot'
include 'samples:hazelcast'
include 'samples:httpsession'
include 'samples:rest'
include 'samples:security'
include 'samples:users'
include 'samples:websocket'