Add auth filter to proxy

master
esensar 2017-06-02 00:12:09 +02:00
parent 41339cf26a
commit 12958470b7
19 changed files with 364 additions and 6 deletions

View File

@ -0,0 +1,5 @@
spring.application.name=eureka-service
# N.B. this is the default:
spring.cloud.config.uri=${CONFIG_SERVER_URI:http://localhost:8888}
spring.cloud.config.username=${CONFIG_SERVER_USERNAME:root}
spring.cloud.config.password=${CONFIG_SERVER_PASSWORD:root}

View File

@ -0,0 +1,5 @@
spring.application.name=steleks-proxy
# N.B. this is the default:
spring.cloud.config.uri=${CONFIG_SERVER_URI:http://localhost:8888}
spring.cloud.config.username=${CONFIG_SERVER_USERNAME:root}
spring.cloud.config.password=${CONFIG_SERVER_PASSWORD:root}

View File

@ -1 +1 @@
include 'common', 'events', 'config_server', 'teams', 'users', 'eureka-service'
include 'common', 'events', 'config_server', 'teams', 'users', 'eureka-service', 'steleks-proxy'

View File

@ -30,6 +30,7 @@ dependencyManagement {
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-config')
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-security')
testCompile('org.springframework.boot:spring-boot-starter-test')
compile('org.springframework.cloud:spring-cloud-starter-eureka')
compile('org.springframework.cloud:spring-cloud-starter-zuul')

View File

@ -4,12 +4,34 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class SteleksProxyApplication {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
public static void main(String[] args) {
SpringApplication.run(SteleksProxyApplication.class, args);
}

View File

@ -0,0 +1,35 @@
package ba.steles.security;/**
* Created by ensar on 01/06/17.
*/
import java.util.Set;
public class AuthResponse {
private Long userId;
private Set<String> userRoles;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Set<String> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<String> userRoles) {
this.userRoles = userRoles;
}
@Override
public String toString() {
return "AuthResponse{" +
"userId=" + userId +
", userRoles=" + userRoles +
'}';
}
}

View File

@ -0,0 +1,76 @@
package ba.steles.security;/**
* Created by ensar on 28/05/17.
*/
import ba.steles.service.Service;
import ba.steles.service.discovery.ServiceDiscoveryClient;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URI;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class AuthenticationFilter extends GenericFilterBean {
private RestTemplate restTemplate;
private ServiceDiscoveryClient discoveryClient;
public AuthenticationFilter(RestTemplate restTemplate, ServiceDiscoveryClient discoveryClient) {
this.restTemplate = restTemplate;
this.discoveryClient = discoveryClient;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
Authentication authentication;
String token = ((HttpServletRequest)request).getHeader("Authorization");
if(token != null) {
try {
System.out.println("token: " + token);
String usersServiceBase = discoveryClient.getServiceUrl(Service.USERS);
AuthResponse usersResponse = restTemplate.getForObject(usersServiceBase + "/accesstoken/{token}", AuthResponse.class, token);
System.out.println("the response= " + usersResponse);
Set<String> userRoleSet = usersResponse
.getUserRoles();
if (userRoleSet == null) {
userRoleSet = new HashSet<>();
userRoleSet.add("NO_ROLES");
}
Set<SimpleGrantedAuthority> roleSet =
userRoleSet
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
authentication = new UsernamePasswordAuthenticationToken("a name", null,
roleSet);
} catch (Exception ex) {
ex.printStackTrace();
authentication = null;
}
} else {
authentication = null;
}
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,41 @@
package ba.steles.security;/**
* Created by ensar on 02/06/17.
*/
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.logging.Logger;
@Component
public class RelayTokenFilter extends ZuulFilter {
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// Alter ignored headers as per: https://gitter.im/spring-cloud/spring-cloud?at=56fea31f11ea211749c3ed22
Set<String> headers = (Set<String>) ctx.get("ignoredHeaders");
// We need our tokens relayed to resource servers
headers.remove("authorization");
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 10000;
}
}

View File

@ -0,0 +1,39 @@
package ba.steles.security;/**
* Created by ensar on 16/05/17.
*/
import ba.steles.service.discovery.ServiceDiscoveryClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestTemplate;
@Configuration
@EnableWebSecurity
@ComponentScan("org.baeldung.security")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Autowired
private ServiceDiscoveryClient discoveryClient;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/users/**", "/users", "/").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new AuthenticationFilter(restTemplateBuilder.build(), discoveryClient), UsernamePasswordAuthenticationFilter.class);
}
}

View File

@ -0,0 +1,41 @@
package ba.steles.service;/**
* Created by ensar on 16/04/17.
*/
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.logging.Logger;
@Primary
@Component
@RefreshScope
public class DefaultServiceIdProvider implements ServiceIdProvider {
private static final Logger logger =
Logger.getLogger(DefaultServiceIdProvider.class.getName());
public static final String NO_SERVICE = "NO_SERVICE";
@Value("${users.name}")
private String usersName;
@Value("${events.name}")
private String eventsName;
@Value("${teams.name}")
private String teamsName;
@Override
public String getServiceId(Service service) {
switch (service) {
case USERS:
return usersName;
case TEAMS:
return teamsName;
case EVENTS:
return eventsName;
default:
return NO_SERVICE;
}
}
}

View File

@ -0,0 +1,11 @@
package ba.steles.service;
/**
* Created by ensar on 16/04/17.
*/
public enum Service {
USERS,
EVENTS,
TEAMS;
}

View File

@ -0,0 +1,9 @@
package ba.steles.service;
/**
* Created by ensar on 16/04/17.
*/
public interface ServiceIdProvider {
String getServiceId(Service service);
}

View File

@ -0,0 +1,48 @@
package ba.steles.service.discovery;
/**
* Created by ensar on 16/04/17.
*/
import ba.steles.service.Service;
import ba.steles.service.ServiceIdProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.logging.Logger;
@Primary
@Component
public class EurekaServiceDiscoveryClient implements ServiceDiscoveryClient {
private static final Logger logger =
Logger.getLogger(EurekaServiceDiscoveryClient.class.getName());
private DiscoveryClient discoveryClient;
private ServiceIdProvider serviceIdProvider;
@Autowired
public EurekaServiceDiscoveryClient(DiscoveryClient discoveryClient, ServiceIdProvider serviceIdProvider) {
this.discoveryClient = discoveryClient;
this.serviceIdProvider = serviceIdProvider;
}
@Override
public String getServiceUrl(Service service) {
String serviceId = serviceIdProvider.getServiceId(service);
List<ServiceInstance> usersInstances = discoveryClient.getInstances(serviceId);
if(usersInstances == null || usersInstances.size() == 0) {
System.err.print(service.toString() + " service not found!");
throw new RuntimeException();
}
ServiceInstance serviceInstance = usersInstances.get(0);
if(serviceInstance == null) {
throw new RuntimeException();
}
return serviceInstance.getUri().toString();
}
}

View File

@ -0,0 +1,11 @@
package ba.steles.service.discovery;
import ba.steles.service.Service;
/**
* Created by ensar on 16/04/17.
*/
public interface ServiceDiscoveryClient {
String getServiceUrl(Service service);
}

View File

@ -71,11 +71,14 @@ public class AuthenticationController {
@RequestMapping(path = "/accesstoken/{token}", method = RequestMethod.GET)
public ResponseEntity<?> validateToken(@PathVariable String token) {
System.out.println("Validating token: " + token);
if (tokenStore.isValidToken(token)) {
System.out.println("Valid token: " + token);
Long userId = tokenStore.getTokenInfo(token);
User user = usersJpaRepository.findOne(userId);
if(user != null) {
System.out.println("Found user with id: " + userId);
Map<String, Object> response = new HashMap<>();
response.put("userId", String.valueOf(userId));
response.put("roles",
@ -85,11 +88,13 @@ public class AuthenticationController {
.ok()
.body(response);
} else {
System.out.println("Found no user with id: " + userId);
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.build();
}
} else {
System.out.println("Invalid token: " + token);
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.build();

View File

@ -29,6 +29,8 @@ public class AuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("Received a request which requires auth!");
Authentication authentication = TokenAuthenticationService.getAuthentication(
(HttpServletRequest) request,
tokenStore,

View File

@ -42,7 +42,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/accesstoken").permitAll()
.antMatchers("/accesstoken", "/accesstoken/**", "/").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new AuthenticationFilter(tokenStore, usersJpaRepository), CustomUrlUsernamePasswordAuthenticationFilter.class);

View File

@ -21,18 +21,23 @@ public class TokenAuthenticationService {
TokenStore tokenStore,
UsersJpaRepository usersJpaRepository) {
String token = request.getHeader(HEADER_STRING);
System.out.println("token: " + token);
System.out.println("headers: " + request.getHeaderNames());
if (token != null && tokenStore.isValidToken(token)) {
Long userId = tokenStore.getTokenInfo(token);
User user = usersJpaRepository.findOne(userId);
if(user != null) {
System.out.println("Found token... userId: " + userId);
return new UsernamePasswordAuthenticationToken(user.getUsername(), null,
UserRoleFactory.toGrantedAuthorities(user.getUserRoles()));
} else {
System.out.println("Cant find token");
return null;
}
}
System.out.println("Cant find token");
return null;
}
}

View File

@ -14,18 +14,20 @@ import java.util.concurrent.TimeUnit;
*/
@Component
public class BasicTokenStore implements TokenStore {
public class BasicInMemoryTokenStore implements TokenStore {
// Default one hour ttl
public static final long DEFAULT_TTL =
TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS);
private KeyValueStore<String, TokenInfo> tokenStore;
private static KeyValueStore<String, TokenInfo> tokenStore;
private long ttl = DEFAULT_TTL;
@Autowired
public BasicTokenStore(KeyValueStore<String, TokenInfo> tokenStore) {
this.tokenStore = tokenStore;
public BasicInMemoryTokenStore(KeyValueStore<String, TokenInfo> tokenStore) {
if(BasicInMemoryTokenStore.tokenStore == null) {
BasicInMemoryTokenStore.tokenStore = tokenStore;
}
}
@Override