I am wondering what the best way is to setup a project that contains a Spring RESTful API along with the ability to serve up static Angularjs pages to consume the RESTful web service. The below implementation works but I am now looking to add security into the application and I am unsure how to apply Spring Security to both the REST Api and the static pages.
Is the below setup correct for my end goal?
How do I secure both the REST Api && the static pages?
I have the following project structure
Servlet Config
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
#ComponentScan
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addFilter("corsFilter", new CORSFilter());
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebMvcConfig.class);
ctx.setServletContext(servletContext);
Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
dynamic.addMapping("/api/*");
dynamic.setLoadOnStartup(1);
}
}
I would follow the series of articles on spring.io blog that explains exactly what you are looking for: Spring + Security + Angular JS.
Here the articles:
http://spring.io/blog/2015/01/12/spring-and-angular-js-a-secure-single-page-application
https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii
http://spring.io/blog/2015/01/20/the-resource-server-angular-js-and-spring-security-part-iii
http://spring.io/blog/2015/01/28/the-api-gateway-pattern-angular-js-and-spring-security-part-iv
http://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v
Related
I have a back-end which is build on spring-boot and then some custom code from my school built upon that.
The front-end is pure angular application which I serve from a different server trough a gulp serve.
They're only connected by REST calls.
There's already an authentication module running on the backend and to now I need to serve this angular application from the same tomcat server the back-end is running on so it can also use this authentication module.
I've found this about multiple connectors so I copied it as following class to set up multiple connectors:
#ConfigurationProperties
public class TomcatConfiguration {
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
//tomcat.addAdditionalTomcatConnectors(createSslConnector());
return tomcat;
}
private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
try {
File keystore = new ClassPathResource("keystore").getFile();
File truststore = new ClassPathResource("keystore").getFile();
connector.setScheme("https");
connector.setSecure(true);
connector.setPort(8443);
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keystore.getAbsolutePath());
protocol.setKeystorePass("changeit");
protocol.setTruststoreFile(truststore.getAbsolutePath());
protocol.setTruststorePass("changeit");
protocol.setKeyAlias("apitester");
return connector;
} catch (IOException ex) {
throw new IllegalStateException("can't access keystore: [" + "keystore"
+ "] or truststore: [" + "keystore" + "]", ex);
}
}
}
Problem is that I don't see or find how I should setup these connectors so they serve from my angularJS build folder.
Upon searching I came upon Spring-Boot : How can I add tomcat connectors to bind to controller but I'm not sure if in that solution I should change my current application or make a parent application for both applications.
My current application main looks like this:
#Configuration
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
If possible I'd like some more info about what connectors are in the spring-boot context.
If this is not the way to go I'd like someone to be able to conform this second solution or suggest a change in my code.
I'm really not sure enough about these solution that I want to go breaking my application over it. (though it's backed up with github)
Just place your AngularJS + other front-end assets into src/main/resources/static folder, Spring Boot will serve them automatically.
I am using the Google Plugin for Eclipse, and I am writing an App Engine app as a Dynamic Web Module in Eclipse WTP.
I have defined the following Java class to serve as a Cloud Endpoint API:
package mypackage;
import static mypackage.OfyService.ofy;
import java.util.List;
import java.util.logging.Logger;
import mypackage.models.ProbeEntry;
import mypackage.models.ProbeSet;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.config.Named;
import com.googlecode.objectify.ObjectifyService;
#Api(name = "analysisEndpoint",
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "myorg",
ownerName = "myorg",
packagePath = "analysis")
)
public class AnalysisEndpoint {
private static final Logger logger = Logger.getLogger(AnalysisEndpoint.class.getName());
#ApiMethod(name = "getMyProbeEntries", httpMethod = ApiMethod.HttpMethod.GET)
public ProbeSet getMyProbeEntries(#Named("amount") int amount) {
ObjectifyService.begin();
List<ProbeEntry> probeList = ofy().load().type(ProbeEntry.class).limit(amount).list();
return new ProbeSet(probeList);
}
}
I attempt to deploy to the Google App Engine by right-clicking the project -> Google App Engine WTP -> Deploy Project to Remote Server. I see in my console that the project is compiling and uploading, but eventually errors out with:
99% Endpoints configuration not updated. The app returned an error when the Google Cloud Endpoints server attempted to communicate with it.
The error log on the app engine shows the following:
18:31:58.119
javax.servlet.ServletContext log: unavailable
com.google.api.server.spi.config.validation.MissingParameterNameException: analysisEndpoint.myorg.analysis.AnalysisEndpoint.getMyProbeEntries parameter (type int): Missing parameter name. Parameter type (int) is not an entity type and thus should be annotated with #Named.
at
com.google.api.server.spi.config.validation.ApiConfigValidator.validateApiParameter(ApiConfigValidator.java:214)
...
As can be seen in the code, I do have #Named("amount") before the offending parameter. What is going wrong here? Side note: If I simply remove the amount parameter, the project deploys to App Engine without a problem.
Any help would be greatly appreciated.
I have a client side application built with AngularJS that is consuming services from a RESTful ASP.NET Web API. So far so good. I have created both of them under the same solution on Visual Studio, the API is an ASP.NET project and the AngularJS is a website. Both projects have to work using windows authorization so I created the API with windows authorization as the default AA mechanism in the project creator wizard, and for the AngularJS I have enable windows authentication on the properties tab of the project.
In order to test the communication between the two applications I decided to build a simple service. I created a Quotation model class, built the controller for it, and then added migrations and added some quotations in the database. I then tried to send a get request from the angular application only to receive this error:
After studying this issue I realized that I had to enable CORS on the web API. So I went to NuGet Package Manager and added the Microsoft.AspNet.Cors package to the project.
I then enabled CORS on the WebApiConfig.cs like this:
namespace Web_API
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
And I added the header to my controller class and method (just in case on the class wasn't enough):
namespace Web_API.Controllers
{
[EnableCors("*", "*","*")]
public class QuotationsController : ApiController
{
private Web_APIContext db = new Web_APIContext();
// GET: api/Quotations
[EnableCors("*", "*", "*")]
public IQueryable<Quotation> GetQuotations()
{
return db.Quotations;
}
However, I still get the same error when I make a get request from the AngularJS application. Does anyone know how to fix this issue?
can you please try this:
[EnableCors(origins: "*", headers: "*", methods: "*")]
Also don't use EnableCors in your method. As you've used this on your controller, by default all methods will fall under this rule.
I hope this will solve your problem. Thanks.
I am very new to google app engine and endpoints and have been writing basic endpoint functions and deploying to the cloud. I succesfully deployed a HelloWorld endpoint and tested it over the API explorer: http://localhost:8080/_ah/api/explorer
But now when I have created a new endpoint API and followed the same steps (i.e deployed using new APP engine application name in the appengine-web.xml, run as appengine:update), the api explorer still shows my HelloWorld endpoint instead of my new API "yourfirstendpoint".
I've searched and tried to find an answer to no avail - and im sorry if this is a very basic and stupid question on my part (im sure it is) but i would realy appreciate if somebody could point me in the right direction on what i should be doing.
My API
package com.example.zinglife;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;
import com.google.api.server.spi.response.NotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
/**
*
* Defines endpoint functions APIs.
*/
#Api(name = "yourfirstapi", version = "v1",
scopes = {Constants.EMAIL_SCOPE },
clientIds = {Constants.API_EXPLORER_CLIENT_ID},
description = "API for hello world endpoints.")
public class YourFirstAPI
{
#ApiMethod(name = "storeUserModel")
private User storeUserModel(User user) throws NotFoundException
{
String email = user.getEmail();
Key key = KeyFactory.createKey("User",email);
User userEntity = null;
try
{
if (userEntity==null)
{
userEntity = new User();
userEntity.setName(user.getName());
userEntity.setEmail(user.getEmail());
userEntity.setCountry(user.getCountry());
//
}
return userEntity;
}//*endtry
finally
{
}
}
}
The App engine Administrator Log after running the code:
Please let me know if any other information is needed :)
Make sure you have added your new service as one of the values for the 'services' parameter of the EndPointsServlet.
<servlet>
<!-- This is version 2.0 of the endpoints framework. -->
<servlet-name>EndpointsServlet</servlet-name>
<servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<!-- Comma separated classes that provide endpoints -->
<param-value>
com.mycompany.myproduct.endpoint.SomeServiceV1,
com.mycompany.myproduct.endpoint.SomeServiceV2,
com.mycompany.myproduct.endpoint.SomeOtherServiceV1,
com.mycompany.myproduct.endpoint.SomeOtherServiceV2,
com.mycompany.myproduct.endpoint.SomeOtherServiceV3
</param-value>
</init-param>
</servlet>
I want to separate packages for UI and backend development of my GWTP app.
Currently my UI access the backend using Rest dispatch configured like this:
bindConstant().annotatedWith(RestApplicationPath.class).to("/MyProject/api");
I want to access remote service using localhost UI (running GWT app using eclipse plugin). I changed the above line to:
bindConstant().annotatedWith(RestApplicationPath.class).to("http://my-app.appspot.com/MyProject/api");
Using this, call successfully reaches server ( I can see this in appengine logs) but UI always gets back status code 0.
What is wrong with above setup? Do I have to do something else to access remote service using GWT ui ?
If you want to have a solution that works both on localhost/App Engine, you'd want to use something like this:
import com.google.gwt.core.client.GWT;
import com.google.gwt.inject.client.AbstractGinModule;
import com.google.inject.Provides;
import com.gwtplatform.dispatch.rest.client.RestApplicationPath;
import com.gwtplatform.dispatch.rest.client.gin.RestDispatchAsyncModule;
public class ServiceModule extends AbstractGinModule {
#Override
protected void configure() {
install(new RestDispatchAsyncModule.Builder().build());
}
#Provides
#RestApplicationPath
String getApplicationPath() {
String baseUrl = GWT.getHostPageBaseURL();
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
}
return baseUrl + "/MyProject/api";
}
}
The string returned by getApplicationPath will be bound to #RestApplicationPath and used seamlessly by GWTP's RestDispatch.
In your case, the string will resolve to http://localhost:8080/MyProject/api or "http://my-app.appspot.com/MyProject/api" depending on the app running locally or on App Engine.