How to handle routing when both front end and back end are on the same server - reactjs

I have a Spring boot and it has some services running on it. It also has a react view that has its own routing. Is this even possible, can I have a backend routing and front end routing within the same server?
Thanks

After some research, front-end routing is a misleading term, although it is being widely used. The routing needs to always go to the back-end, and the back-end decides what happens with the request. If React.js, Angular or any other front-end frameworks are handling routing, back-end needs to forward all the requests that are coming from the front-end to the front-end again, which is the HTML page that has the JS and CSS. To achieve this with spring framework, I used a tuckey filter to forward requests to the html page. The magic word is FORWARDING the request to the html page.
The urlrewrite.xml for the tuckey filter forwards the requests to the ui again, it looks like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN" "http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<!-- Configuration file for UrlRewriteFilter http://www.tuckey.org/urlrewrite/ -->
<urlrewrite>
<rule match-type="regex">
<name>Front-end forwarding</name>
<note>Forwarding all routes from the front-end to the ui controller</note>
<condition type="request-uri" operator="notequal">^\/([\-\w\.]+)([\-/\w\.]*)(\.([\-\w]+))$</condition>
<from>^\/ui\/([/\-\w]+)$</from>
<to type="forward" last="true">/ui</to> <!--This is where the ui controller is located-->
</rule>
</urlrewrite>
This is the tuckey filter java configurations:
#Component
public class TuckeyFilterConfigurations extends UrlRewriteFilter{
private static final String CONFIG_LOCATION = "classpath:/urlrewrite.xml";
#Value(CONFIG_LOCATION)
private Resource resource;
#Override
protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
Conf conf = new Conf(filterConfig.getServletContext(), resource.getInputStream(), resource.getFilename(), "");
checkConf(conf);
} catch (IOException ex) {
throw new ServletException("Unable to load URL-rewrite configuration file from " + CONFIG_LOCATION, ex);
}
}
}
And finally, the controller that serves the html page simply looks like:
#Controller
public class UiController {
#RequestMapping("/ui")
public String entrance(){
return "index.html";
}
}

Yes you can have but it's always a good practice to have the routing logic at one place, as it helps a lot in troubleshooting the application once it's size grows.
How this would work, can you give an example? When I have a front end routing to a certain link, it is always directed to the backend. I know that there is the # routing, but I'm looking for a solution for the regular "/" routing?
You can use BrowserRouter or StaticRouter to achieve it. Here is the nice guide by React training team to implement Server Rendering using StaticRouter - Server Rendering with React.

Step 1 add below in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
Step 2, Please add below two lines in your application.properties :
#reload static content lively without reboot spring boot app
spring.devtools.livereload.enabled: true
#specify the static file location
spring.resources.static-locations: file:${your-frontend-file-root-location}

Related

React page not rendering when using Okta

I working on React app which is secured by Okta OAuth provider. Currently, all of my routes are authenticated. I want a public route where anyone can access it. Eg: https://my.example.com/welcome
These are the configs that I have used
application.yml
okta:
oauth2:
issuer: https://xxxxxxxxx.okta.com/oauth2/default
clientId: xxxxxxxxxxxxxx
clientSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
post-logout-redirect-uri: /
redirect-uri: /login/callback
Spring security config
httpSecurity.authorizeRequests()
.antMatchers("/", "/welcome").permitAll()
.anyRequest().authenticated()
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.and().oauth2Client()
.and().oauth2Login();
return httpSecurity.build();
React Route
<Route path={"/welcome"} exact={true} render={(props) => (<WelcomePage/>)}/>
When I visit /welcome route I'm getting a blank page with this warning message in the browser
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://xxxxxxx.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=xxxxxxxxx&scope=profile%20email%20openid&state=rDYLqV7WDv2la1onSvQsTNeXCvDmWhS0_ZoPFlMAE80%3D&redirect_uri=https://xxxxxx.xxxxx.com/careportal/login/callback&nonce=XvMoC5iP5OGYsvG0bS-QfQh1yfYmhNohmD7GvfukyCo with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details.
Can anyone help me to access a public route without auth?
TIA
If you have anything else than spring-boot-starter-oauth2-resource-server as OAuth2 lib in your Spring project, remove it and configure your Spring REST API as a resource-server.
With the helper lib from second tutorial, this might be enough:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- replace "webmvc" with "weblux" for reactive app -->
<!-- replace "jwt" with "introspecting" if Okta access-tokens are not JWTs -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<version>6.0.4</version>
</dependency>
#EnableMethodSecurity // replace with #EnableReactiveMethodSecurity for reactive app
#Configuration
public class SecurityConfig {
}
com.c4-soft.springaddons.security.issuers[0].location=https://xxxxxxxxx.okta.com/
com.c4-soft.springaddons.security.issuers[0].authorities.claims=groups
com.c4-soft.springaddons.security.permit-all=/,/welcome
# following might be too permissive, restrict if needed
com.c4-soft.springaddons.security.cors[0].path=/**
With Spring spring-boot-starter-oauth2-resource-server only, there is quite some more Java conf to write (refer to first tutorial)
Do not forget to request groups (and maybe openid and offline_access) scopes when you authenticate users from your React app or other clients like Postman.

Spring boot + (Thymeleaf is overriding React JS views)

I have created an error page with Thymeleaf and I use it that way because I can send error messages to users through the controller.
#ExceptionHandler(Exception.class)
public ModelAndView controllerExceptionHandler(
Exception e,
HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
String[] messages = e.getMessage().split("</br>");
mav.addObject("message", messages);
mav.addObject("timestamp", new Date());
mav.addObject("url", request.getRequestURL());
mav.addObject("headerMessage", "Error :(");
mav.addObject("contentMessage", "We are working hard to resolve it.");
mav.setViewName("error");
mav.addObject("status", 500);
return mav;
}
In template file I have something like this:
<h1 class="mr-3 pr-3 align-top border-right inline-block align-content-center" th:text="${status}">404</h1>
<div class="inline-block align-middle">
<div>
<h1>Something went wrong...</h1>
</div>
<th:block th:each="msg : ${message}">
<h2 class="font-weight-normal lead" id="desc" th:text="${msg}">The page you requested was not found.</h2>
</th:block>
</div>
Previously I had a simple React JS application also running with Spring Boot and Thymeleaf and then I used Thymeleaf template to show that. I had a template index.html where was actually React JS build file, so every time I had to copy the build file inside there, JS and CSS files into a static folder (after build). Now the React APP got more complex and I decided to use frontendmaven plugin to build it straight away with backend.
How to tell Spring Boot to not try to use Thymeleaf when resolving ReactJS views? This is how I serve ReactJS views.
#RequestMapping("/")
public String index() {
return "index.html";
}
Or would it be possible to get rid of Thymeleaf? Is it possible to send variables to ReactJS views through Java controllers when serving those views? The modelandview example?
How to tell Spring Boot to not try to use Thymeleaf when resolving
React JS views?
Remove ThymeleafViewResolver in webconfiguration and switch to Rest api ( #Restcontroller instead of #Controller ). This way you are telling Spring for not to render a view instead act as api-endpoints.
Now you can update your react code to call these Spring rest apis, prebuilt using maven-frontend-plugin and deploy.
Now the question comes, what is the stating point for your application
Only for this purpose, you can create a single controller which will handle request to "/" and will return index page residing under resources/template folder. This index.html page will be using your prebuilt react pages as -
<script src="built/bundle.js"></script>
Demo application: https://github.com/ankidaemon/Spring5-ReactJS/tree/master/Section5/Video5.3/SpringSecurity-Reactjs-RestAPI
Is it possible to send variables to React JS views through Java
controllers when serving those views
This is called server-side-rendering, however for react this is different then jsp and freemarker, thymeleaf etc and I would say not an easy way to do it with react. You can try your luck with this -> https://codeburst.io/jsx-react-js-java-server-side-rendering-ssr-2018-cf3aaff7969d

Spring boot & Spring Security : Forward the invalid urls

I work with spring boot, thymeleaf, spring security and angularjs
I want all url invalid to return to the home page.
The login page is managed by thymeleaf and the rest by angularjs
So i used a #Controller to forward all invalid url to the localhost:8080/:
#Controller
public class AccueilController {
// Match everything without a suffix (so not a static resource)
#RequestMapping(value = "/{path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
when connecting if I type localhost:8080/XXXXthat forward me to the racine after login but if I type localhost:8080/XXXX/XXXX that give me a 404 error
after connection I don't have that problem because Angular forward all url correctly.
That's not the default behavior of the web applications. I sugest you to customize the not found page (Spring Boot and custom 404 error page) and then from there create a link to your home;

resolving views in angularjs spring boot application with spring security

I am trying to build an application with AngularJS 1.x, Spring Boot REST with Spring Security. Though I have intermediate knowledge on Spring REST itself, I am quite new to AngularJS and Spring Security.
I have given the github link of the application I have developed so far. Its in skeletal form still, and nothing is working:
https://github.com/imrnk/touchinghand
I will list down the problems and confusion I am having below:
1) I am using ui-router states to navigate from one state to another. So far I have identified two states: the "login" page and a link from there to "registration" page. Once the user will logged in, she will land to a dashboard. But this dashboard is yet to be created.
Now this login.html could be said as the entry point to the application. And when I type localhost:8080/ it should redirect to localhost:8080/login. Now I can see the page is redirecting correctly to the login.html but the templates I am using (login.template.html or register.template.html) inside login.html is not loading... However, when I am running through node js using browsersync, I see the page is loading with all the contents in it.
2) I tried disabling the spring security and then I see the login page is loaded correctly. So I guessed it could be a spring security issue, but what exactly is the issue I couldn't figure out.
My HttpSecurity configuration inside the implementation of WebSecurityConfigurerAdapter looks like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login", "/register", "/**/*.css",
"/**/*.js", "/**/**/*.css", "/**/**/*.js",
"/**/**/**/*.css", "/**/**/**/*.js",
"/**/**/**/**/*.css", "/**/**/**/**/*.js", "/**/home.html", "**/login.html",
"**/**/login.template.html", "**/**/registration.template.html")
.permitAll().
regexMatchers("/registration*")
.permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
}
And my MvcConfig looks like this:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("classpath:/resources/static/css/**")
.addResourceLocations("classpath:/resources/static/css/");
registry.addResourceHandler("classpath:/resources/js/**").addResourceLocations("classpath:/resources/js/");
registry.addResourceHandler("classpath:/resources/static/**")
.addResourceLocations("classpath:/resources/static/");
registry.addResourceHandler("classpath:/resources/static/templates/**")
.addResourceLocations("classpath:/resources/static/templates/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/").setViewName("forward:/login");
//registry.addViewController("/login").setViewName("forward:/login");
registry.addViewController("login.html");
//registry.addViewController("register").setViewName("registration");
// registry.addViewController("/registration.html");
//registry.addViewController("/dashboard").setViewName("dashboard");
//registry.addViewController("/dashboard.html");
}
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("static/");
resolver.setSuffix(".html");
registry.viewResolver(resolver);
}
}
My LoginController class which anotated as #Controller has this:
#RequestMapping(value={"/"}, method = RequestMethod.GET)
public String showLogin(Model model){
model.addAttribute("user", new UserDTO());
return "/login";
}
3) I am quite confused between what should be the name of the resolved views and how that could map with the angular templates html.
I suspect, probably I am following a pure REST approach, and hanging in the middle with some aspect of Spring MVC and Spring REST. Introducing Spring Security also increased the problem for now.
I am also attaching the firefox developer network screenshot if that could help
enter image description here
Kindly help me out. Thanks.

Spring-boot: add application to tomcat server

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.

Resources