I am using Spring session and Spring security for my REST API, but encountered a problem when I enabled CORS via a simple filter.
If I used a relative URI by http proxy(map http://xxxx/api to /api in client app), it works well.
If I used the full URL directly, I encountered a problem when used CORS, it can not fetch the session info, the following is the Spring security log.
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.FilterChainProxy - /api/mgt/appupdates at position 1 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.FilterChainProxy - /api/mgt/appupdates at position 2 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
2015-02-11 10:46:57,745 [http-nio-8080-exec-29] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
I am using the Spring stack, including Spring 4.1.4.RELEASE, Spring Security 4, Spring Session 1.0.0.RELEASE, etc
Spring session config:
#Configuration
#EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60 * 120 )
public class RedisHttpSessionConfig {
#Bean
public HttpSessionStrategy httpSessionStrategy(){
return new HeaderHttpSessionStrategy();
}
}
The Http Session initializer class content:
#Order(100)
public class RedisHttpSessionApplicationInitializer
extends AbstractHttpSessionApplicationInitializer {}
The RedisHttpSessionConfig is loaded in my web initializer(#Order(0)). And there is another Initializer for Spring Security(#Order(200)).
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
private static final Logger log = LoggerFactory.getLogger(SecurityInitializer.class);
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", DelegatingFilterProxy.class);
corsFilter.addMappingForUrlPatterns(
EnumSet.of(
DispatcherType.ERROR,
DispatcherType.REQUEST,
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.ASYNC),
false,
"/*"
);
I have resolved the problem. I moved the doFilter method into a else block.
#Named("corsFilter")
public class SimpleCorsFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(SimpleCorsFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (log.isDebugEnabled()) {
log.debug("call doFilter in SimpleCORSFilter #");
}
response.setHeader("Access-Control-Allow-Origin", "*");
// response.addHeader("X-FRAME-OPTIONS", "SAMEORIGIN");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
if (log.isDebugEnabled()) {
log.debug("do pre flight...");
}
response.setHeader("Access-Control-Allow-Methods", "POST,GET,HEAD,OPTIONS,PUT,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Content-Type,Accept,x-auth-token,x-xsrf-token,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin");
//response.setHeader("Access-Control-Expose-Headers", "Access-Control-Allow-Origin,x-auth-token");
} else {
filterChain.doFilter(request, response);
}
}
}
Thus the doFilter only executes in none OPTIONS method. This solution overcomes this barrier temporarily. I think this could be a Spring Session related bug.
A CORS filter needs to know about the Spring Session header in the list of allowed request headers. HeaderHttpSessionStrategy defines this as "x-auth-token" but it can be changed. Note that any header starting with "x-" is treated as application specific, a tell-tale sign you need to configure your CORS filter to allow it.
May it add a #CrossOrigin to your request method help? like below:
#GetMapping("/path")
#CrossOrigin
public String detail(#PathVariable("id") Integer activityId){
return "";
}
Related
Is it possible to remove response headers with a RestFilter? Looking at this cookbook you would say it should be possible. However, the filter is only called when the request is incoming, before the call to the resource class. I was expecting to have a hook where I can modify the response headers before sending it back to the client.
i had a look at CORSFilter as an example, but it only sets headers, not remove them.
To be more specific, I want to remove the WWW-Authenticate header that is set by the Auth provider when the session has expired. This header causes a popup in the browser (chrome) that is undesirable.
what you need is a javax.ws.rs.container.ContainerRequestFilter. In jax-rs such filters can be registered in a javax.ws.rs.core.Application. The application used in ICM is com.intershop.component.rest.internal.application.DefaultRestApplication which can be adapted using an com.intershop.component.rest.internal.application.ApplicationClassesProvider that can be registered using a Set-Binding.
So you could create a Guice-Module and your filter:
public class MyRestModule extends AbstractModule
{
#Override
protected void configure()
{
Multibinder<ApplicationClassesProvider> binder = Multibinder.newSetBinder(binder(),
ApplicationClassesProvider.class);
binder.addBinding().toInstance(c->c.accept(MyResponseFilter.class));
}
}
public class MyResponseFilter extends ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
{
response.getHeaders().remove("WWW-Authenticate");
}
}
Please note that this filter will be applied to all requests, so please make sure you remove headers only for requests you really care about.
I am aware that we can force FeignClient to use OkHttp instead of Ribbon by providing the url Ex. #FeignClient(url="serviceId", name="serviceId")
I want the OkHttpClient to be used even when just the name is provided. Ex. #FeignClient(name="serviceId")
As per the spring cloud documentation "if Ribbon is enabled it is a LoadBalancerFeignClient, otherwise the default feign client is used."
How can I disable ribbon so that the default feign client will be used.
None of the solutions on the internet worked for me.
Simply setting an absolute url in the url portion resulted in loadbalancing exceptions
// this resulted in java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: localhost
#Lazy
#Configuration
#Import(FeignClientsConfiguration.class)
public class MyConfig {
#LocalServerPort
private int port;
#Bean
public MyClient myClient(final Decoder decoder, final Encoder encoder, final Client client) {
return Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.target(MyClient.class, "http://localhost:" + localServerPort);
}
}
setting spring.cloud.loadbalancing.ribbon.enabled=false resulted in application context problems. Additional settings needs to be disabled for this to work. I did not probe further
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'eurekaLoadBalancerClientConfiguration': Invocation of init method failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:416)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
...
...
My working solution
Finally, after inspecting the source code in org.springframework.cloud.openfeign.ribbon.DefaultFeignLoadBalancedConfiguration, I came up with this solution
#Lazy // required for #LocalServerPort to work in a #Configuration/#TestConfiguration
#TestConfiguration
#Import(FeignClientsConfiguration.class)
public class MyConfig {
#LocalServerPort
private int port;
#Bean
public MyClient myClient(Decoder decoder, Encoder encoder, Client client, Contract contract) {
return Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.target(MyClient.class, "http://localhost:" + localServerPort);
}
// provide a default `FeignClient` so that Spring will not automatically create their LoadBalancingFeignClient
#Bean
public Client feignClient(SpringClientFactory clientFactory) {
return new Client.Default(null, null);
}
}
I had the same question but my setup is a bit different and I did not get it working in my case (using spring-cloud-starter-openfeign with spring mvc style annotations).
FYI: I needed a custom client with an SSLSocketFactory and ended up just creating the bean for the client and keeping the url on #FeignClient
#Bean
public Client myClient() {
return new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
}
However, we do have projects using spring-cloud-starter-feign where the URL is not provided on the annotation. Not sure if the config below is complete (I did not set it up) but it might point you in the right direction...
dependencies
compile("org.springframework.cloud:spring-cloud-starter-feign") {
exclude group: 'org.springframework.cloud', module: 'spring-cloud-starter-ribbon'
exclude group: 'org.springframework.cloud', module: 'spring-cloud-starter-archaius'
}
config
#Configuration
#Import(FeignClientsConfiguration.class) // org.springframework.cloud.netflix.feign.FeignClientsConfiguration
public class MyConfig {
#Value("${client.url}")
private String url;
#Bean
public MyClient myClient(final Decoder decoder, final Encoder encoder, final Client client) {
return Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.target(MyClient.class, url);
}
}
It has nothing to do with Ribbon.
Check this:
feign:
httpclient:
enabled: false
This will disable the spring cloud autoconfigured httpclient, and will search a #Bean named httpClient in the context. So provide the definition of #Bean in a #Configuration class and that's all.
Check class FeignAutoConfiguration in spring cloud feign.
https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html
I implemented rest web services with Spring. When I deployed it in Eclipse as a Spring Boot Application, it works. However when I deployed it in Tomcat 7 on the same machine, it does not work. The error message is as follows:
XMLHttpRequest cannot load http://localhost:8080/ristoreService/oauth/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8081' is therefore not allowed access.
My CORS filter looks like this:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8081");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, "
+ "Origin,Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
If I comment out response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8081");, I still get the same error. It wouldn't work without this line even if I deploy in Eclipse. Why does it act differently being deployed under different environment on the same ip?
EDIT:
I tested the url http://localhost:8080/ristoreService/oauth/tokenwith rest client tester "CocoaRestClient" and got 404. So I made up a url which apparently does not exist http://localhost:8080/xxxxx and run it in UI (angularjs) and again got the CORS error. I think the error is kind of misleading, it is after all a 404. But why does it complain not found when the war was deployed successfully with the name ristoreService.war under webapps in Tomcat?
Try using a FilterRegistrationBean. Looks like this in Java Config:
#Bean
public FilterRegistrationBean authorizationFilter(){
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(authorizationFilter);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/v1/*");
filterRegBean.setUrlPatterns(urlPatterns);
return filterRegBean;
}
Any reason why you're not using Spring Boot's CORS capabilities? It's already supported out of the box, you just gotta configure it. You can enable it globally like this:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
According to How to deploy Spring Boot application, I have to make main application to extend SpringBootServletInitializer. Once I added that, it works.
To solve CORS issue I used #CrossOrigin. And I did not implement my own CORS filter. Any way spring already have provided few addition solutions for CORS issue.
If you need only your filter you could use it in this way:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(yourFilter);
...
}
There are lots of guidelines, sample codes that show how to secure REST API with Spring Security, but most of them assume a web client and talk about login page, redirection, using cookie, etc. May be even a simple filter that checks for the custom token in HTTP header might be enough. How do I implement security for below requirements? Is there any gist/github project doing the same? My knowledge in spring security is limited, so if there is a simpler way to implement this with spring security, please let me know.
REST API served by stateless backend over HTTPS
client could be web app, mobile app, any SPA style app, third-party APIs
no Basic Auth, no cookies, no UI (no JSP/HTML/static-resources), no redirections, no OAuth provider.
custom token set on HTTPS headers
The token validation done against external store (like MemCached/Redis/ or even any RDBMS)
All APIs need to be authenticated except for selected paths (like /login, /signup, /public, etc..)
I use Springboot, spring security, etc.. prefer a solution with Java config (no XML)
My sample app does exactly this - securing REST endpoints using Spring Security in a stateless scenario. Individual REST calls are authenticated using an HTTP header. Authentication information is stored on the server side in an in-memory cache and provides the same semantics as those offered by the HTTP session in a typical web application. The app uses the full Spring Security infrastructure with very minimum custom code. No bare filters, no code outside of the Spring Security infrastructure.
The basic idea is to implement the following four Spring Security components:
org.springframework.security.web.AuthenticationEntryPoint to trap REST calls requiring authentication but missing the required authentication token and thereby deny the requests.
org.springframework.security.core.Authentication to hold the authentication information required for the REST API.
org.springframework.security.authentication.AuthenticationProvider to perform the actual authentication (against a database, an LDAP server, a web service, etc.).
org.springframework.security.web.context.SecurityContextRepository to hold the authentication token in between HTTP requests. In the sample, the implementation saves the token in an EHCACHE instance.
The sample uses XML configuration but you can easily come up with the equivalent Java config.
You're right, it isn't easy and there aren't many good examples out there. Examples i saw made it so you couldn't use other spring security stuff side by side. I did something similar recently, here's what i did.
You need a custom token to hold your header value
public class CustomToken extends AbstractAuthenticationToken {
private final String value;
//Getters and Constructor. Make sure getAutheticated returns false at first.
//I made mine "immutable" via:
#Override
public void setAuthenticated(boolean isAuthenticated) {
//It doesn't make sense to let just anyone set this token to authenticated, so we block it
//Similar precautions are taken in other spring framework tokens, EG: UsernamePasswordAuthenticationToken
if (isAuthenticated) {
throw new IllegalArgumentException(MESSAGE_CANNOT_SET_AUTHENTICATED);
}
super.setAuthenticated(false);
}
}
You need a spring security filter to extract the header and ask the manager to authenticate it, something like thisemphasized text
public class CustomFilter extends AbstractAuthenticationProcessingFilter {
public CustomFilter(RequestMatcher requestMatcher) {
super(requestMatcher);
this.setAuthenticationSuccessHandler((request, response, authentication) -> {
/*
* On success the desired action is to chain through the remaining filters.
* Chaining is not possible through the success handlers, because the chain is not accessible in this method.
* As such, this success handler implementation does nothing, and chaining is accomplished by overriding the successfulAuthentication method as per:
* http://docs.spring.io/autorepo/docs/spring-security/3.2.4.RELEASE/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#successfulAuthentication(javax.servlet.http.HttpServletRequest,%20javax.servlet.http.HttpServletResponse,%20javax.servlet.FilterChain,%20org.springframework.security.core.Authentication)
* "Subclasses can override this method to continue the FilterChain after successful authentication."
*/
});
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String tokenValue = request.getHeader("SOMEHEADER");
if(StringUtils.isEmpty(tokenValue)) {
//Doing this check is kinda dumb because we check for it up above in doFilter
//..but this is a public method and we can't do much if we don't have the header
//also we can't do the check only here because we don't have the chain available
return null;
}
CustomToken token = new CustomToken(tokenValue);
token.setDetails(authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(token);
}
/*
* Overriding this method to maintain the chaining on authentication success.
* http://docs.spring.io/autorepo/docs/spring-security/3.2.4.RELEASE/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#successfulAuthentication(javax.servlet.http.HttpServletRequest,%20javax.servlet.http.HttpServletResponse,%20javax.servlet.FilterChain,%20org.springframework.security.core.Authentication)
* "Subclasses can override this method to continue the FilterChain after successful authentication."
*/
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
//if this isn't called, then no auth is set in the security context holder
//and subsequent security filters can still execute.
//so in SOME cases you might want to conditionally call this
super.successfulAuthentication(request, response, chain, authResult);
//Continue the chain
chain.doFilter(request, response);
}
}
Register your custom filter in spring security chain
#Configuration
public static class ResourceEndpointsSecurityConfig extends WebSecurityConfigurerAdapter {
//Note, we don't register this as a bean as we don't want it to be added to the main Filter chain, just the spring security filter chain
protected AbstractAuthenticationProcessingFilter createCustomFilter() throws Exception {
CustomFilter filter = new CustomFilter( new RegexRequestMatcher("^/.*", null));
filter.setAuthenticationManager(this.authenticationManagerBean());
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//fyi: This adds it to the spring security proxy filter chain
.addFilterBefore(createCustomFilter(), AnonymousAuthenticationFilter.class)
}
}
A custom auth provider to validate that token extracted with the filter.
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
CustomToken token = (CustomToken)auth;
try{
//Authenticate token against redis or whatever you want
//This i found weird, you need a Principal in your Token...I use User
//I found this to be very redundant in spring security, but Controller param resolving will break if you don't do this...anoying
org.springframework.security.core.userdetails.User principal = new User(...);
//Our token resolved to a username so i went with this token...you could make your CustomToken take the principal. getCredentials returns "NO_PASSWORD"..it gets cleared out anyways. also the getAuthenticated for the thing you return should return true now
return new UsernamePasswordAuthenticationToken(principal, auth.getCredentials(), principal.getAuthorities());
} catch(Expection e){
//TODO throw appropriate AuthenticationException types
throw new BadCredentialsException(MESSAGE_AUTHENTICATION_FAILURE, e);
}
}
#Override
public boolean supports(Class<?> authentication) {
return CustomToken.class.isAssignableFrom(authentication);
}
}
Finally, register your provider as a bean so the authentication manager finds it in some #Configuration class. You probably could just #Component it too, i prefer this method
#Bean
public AuthenticationProvider createCustomAuthenticationProvider(injectedDependencies) {
return new CustomAuthenticationProvider(injectedDependencies);
}
The code secure all endpoints - but I'm sure that you can play with that :). The token is stored in Redis using Spring Boot Starter Security and you have to define our own UserDetailsService which you pass into AuthenticationManagerBuilder.
Long story short - copy paste EmbeddedRedisConfiguration and SecurityConfig and replace AuthenticationManagerBuilder to your logic.
HTTP:
Requesting token - sending basic HTTP auth content in a request header. A token is given back in a response header.
http --print=hH -a user:password localhost:8080/v1/users
GET /v1/users HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Authorization: Basic dXNlcjpwYXNzd29yZA==
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.3
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 4
Content-Type: text/plain;charset=UTF-8
Date: Fri, 06 May 2016 09:44:23 GMT
Expires: 0
Pragma: no-cache
Server: Apache-Coyote/1.1
X-Application-Context: application
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
x-auth-token: cacf4a97-75fe-464d-b499-fcfacb31c8af
Same request but using token:
http --print=hH localhost:8080/v1/users 'x-auth-token: cacf4a97-75fe-464d-b499-fcfacb31c8af'
GET /v1/users HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.3
x-auth-token: cacf4a97-75fe-464d-b499-fcfacb31c8af
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 4
Content-Type: text/plain;charset=UTF-8
Date: Fri, 06 May 2016 09:44:58 GMT
Expires: 0
Pragma: no-cache
Server: Apache-Coyote/1.1
X-Application-Context: application
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
If you pass wrong username/password or token you get 401.
JAVA
I added those dependencies into build.gradle
compile("org.springframework.session:spring-session-data-redis:1.0.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-web")
compile("com.github.kstyrc:embedded-redis:0.6")
Then Redis configration
#Configuration
#EnableRedisHttpSession
public class EmbeddedRedisConfiguration {
private static RedisServer redisServer;
#Bean
public JedisConnectionFactory connectionFactory() throws IOException {
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
redisServer.start();
return new JedisConnectionFactory();
}
#PreDestroy
public void destroy() {
redisServer.stop();
}
}
Security config:
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.httpBasic();
}
#Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
}
Usually in tutorials you find AuthenticationManagerBuilder using inMemoryAuthentication but there is a lot more choices (LDAP, ...) Just take a look into class definition. I'm using userDetailsService which requires UserDetailsService object.
And finally my user service using CrudRepository.
#Service
public class UserService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserAccount userAccount = userRepository.findByEmail(username);
if (userAccount == null) {
return null;
}
return new User(username, userAccount.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
Another Example Project which uses JWT - Jhipster
Try Generating a Microservice application using JHipster. It generates a template with out of the box integration between Spring Security and JWT.
https://jhipster.github.io/security/
I recommend JSON Web Tokens http://jwt.io/ , it's stateless and scalable.
Here is an example project, https://github.com/brahalla/Cerberus
I am creating a REST service using jersey 2.0. I am extending WebApplicationException
Method raising a particular exception
if(json.equals("") || json.equals(" ")) {
throw new ArgumentException("bad post data");
}
public class ArgumentException extends RestException {
.....
public ArgumentException(String message) {
super(Status.BAD_REQUEST,message);
}
}
public class RestException extends WebApplicationException {
...........
public RestException(Status status, String message) {
super(Response.status(status)
.entity(message)
.type("text/plain")
.build());
/*
super(Response.status(status)
.entity(new ErrorBean(status.getStatusCode(),message))
.type(MediaType.APPLICATION_JSON)
.build()); */
}
ErrorBean is a POJO
The method that returns error as plain string inside RestException works (right http code 400 and message). However when I try to pass the ErrorBean POJO and use MediaType.APPLICATION_JSON in response I get an error saying "Headers have already been sent" with http error code 500 (so some internal problem with plumbing) and empty response.
I have also looked at this question Returning JSON or XML for Exceptions in Jersey
How can I return the exception with code and message as a JSON like
{"code" : 400, "message" : .... }
Update
I have received answer on SO as well as jersey users mailing list. steps are
A non AJXB POJO does not need any annotations
Register JacksonFeature in your application
ResourceConfig rc = new ResourceConfig().packages("test").register(JacksonFeature.class);
You need to register JacksonFeature in your Application/ResourceConfig, i.e.:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jackson")
.register(JacksonFeature.class)
// No need to register this provider if no special configuration is required.
.register(MyObjectMapperProvider.class);
Take a look at the documentation for Jackson support in Jersey and also at the example.