Add auth filter to proxy
parent
41339cf26a
commit
12958470b7
|
@ -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}
|
|
@ -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}
|
|
@ -1 +1 @@
|
|||
include 'common', 'events', 'config_server', 'teams', 'users', 'eureka-service'
|
||||
include 'common', 'events', 'config_server', 'teams', 'users', 'eureka-service', 'steleks-proxy'
|
|
@ -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')
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package ba.steles.service;
|
||||
|
||||
/**
|
||||
* Created by ensar on 16/04/17.
|
||||
*/
|
||||
|
||||
public enum Service {
|
||||
USERS,
|
||||
EVENTS,
|
||||
TEAMS;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package ba.steles.service;
|
||||
|
||||
/**
|
||||
* Created by ensar on 16/04/17.
|
||||
*/
|
||||
|
||||
public interface ServiceIdProvider {
|
||||
String getServiceId(Service service);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
Reference in New Issue