As a full stack developer, we need to create an application with both frontend-end and backend.
Today we will create a simple full stack application which will have a social login using Google authentication.The area on which we want to focus is how to integrate google social authentication to spring-boot, with logout functionality. And when angular 4 is used in spring-boot application to build the UI, how we will manage the routes, as we know that both angular and have routes, so how our application will resolve which route it has to call.
Tech Stack
- Angular 4
- SpringBoot
- Gradle
Prerequisites
Let’s start creating an application
- Create a simple spring boot application using Spring.io
- Project Type with gradle and Java
- Add necessary description, group, and artifact
- Add following dependencies to it.
compile 'org.springframework.cloud:spring-cloud-starter-config'
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.springframework.boot:spring-boot-starter-tomcat")
compile 'org.springframework.security:spring-security-web'
compile 'org.springframework.security:spring-security-config'
compile 'org.springframework.security.oauth:spring-security-oauth2'
compile 'org.apache.httpcomponents:httpclient'
compile("io.springfox:springfox-swagger2:2.6.1")
compile("io.springfox:springfox-swagger-ui:2.6.1")
testCompile('org.springframework.boot:spring-boot-starter-test')
compile group: 'org.slf4j' , name: 'slf4j-api' , version: '1.7.22'
compile group: 'io.jsonwebtoken' , name: 'jjwt' , version: '0.7.0'
compile group: 'commons-io' , name: 'commons-io', version: '2.5'
compile group: 'org.projectlombok' , name: 'lombok' , version: '1.16.12'
These dependencies will allow us to enable the google authentication on the top of normal spring boot application.
- Create the property file application-local.yml in the java main resources.
security:
oauth2:
client:
clientId: ******.apps.googleusercontent.com
clientSecret: ********
accessTokenUri: https://www.googleapis.com/oauth2/v4/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth
clientAuthenticationScheme: form
scope:
- openid
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
preferTokenInfo: true
user:
role: test-role
We have created a property file to enable google authentication against the particular client.Replace the * with your actual data.
- Create bootstrap.yml with following data.
<pre>spring:
application:
name: frontend
profiles:
active: local
cloud:
config:
uri: http://localhost:8888
enabled: true</pre>
It will help us to give the name to our application and will activate “LOCAL” profile.
- Create a Security Config java file “SpringConfig” on the same level as of the main file.
package com.test.practise;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers( "/login","/public/**", "/resources/**","/resources/public/**")
.permitAll()
.antMatchers("/","/api/**").authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/");
}
}
It will apply authentication to all the path accept /login, /public, /resources/public/.
- Add controller for logout “SecurityController”
<pre>package com.test.practise.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class SecurityController {
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/";
}
}
</pre>
It will enable the logout URL, which will delete the session.
- We are not pretty much done with the spring side coding, but we have not added any view or frontend related code yet. We will create the angular project now.
- Go to the root of the project and create the angular project angular-cli.
ng new webui
It will create a new project directory for frontend inside our current project directory.
- Navigate to a webui directory which you have created, and make 3 testing controller using the following command.
ng g component landingComponent
ng g component homeComponent
ng g component settingComponent
These are the dummy component to show the routing.
- Now we will mention our routes in the angular project in file app.module.ts
<pre>import {RouterModule, Routes} from "@angular/router";</pre>
const appRoutes: Routes =[
{path:'',component:LandingComponent},
{path:'welcome',component:WelcomeComponent},
{path:'setting',component:SettingComponent}
]
- Add routing module in Imports in the same file.
<pre>@NgModule({
declarations: [
AppComponent,
WelcomeComponent,
SettingComponent,
LandingComponent
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes,{enableTracing:true})
],
providers: [],
bootstrap: [AppComponent]
})
</pre>
- replace the code of app.component.html the main page of ui with following code.
<pre><!–The content below is only a placeholder and can be replaced.–>
<div style="text-align:center">
<h1>
Welcome to {{title}}!!
</h1>
<img width="300" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo=">
</div>
<a href="/setting">Direct Setting</a>
<a href="/welcome">Direct Welcome</a>
<nav>
<a routerLink="/" routerLinkActive="active">Landing Page</a>
<a routerLink="/welcome" routerLinkActive="active">Welcome Page</a>
<a routerLink="/setting" routerLinkActive="active">Setting Page</a>
</nav>
<router-outlet></router-outlet>
</pre>
It will create the routes .First two route for setting and welcome is using href. normal HTML routes and then we have refined routes using routerLink.
- Build the frontend application
ng build
- Now we will make changes in spring application to consume this frontend code.First of all, we will add the code in our build.gradle to copy the frontend code and place it in resource so that it can be consume by spring-boot.
- Add following task to build.gradle
<pre>
task removeWebui(type: Delete) {
delete "${sourceSets.main.resources.srcDirs[0]}/webui"
}
task copyWebui(type: Copy) {
from "webui/dist"
into "${sourceSets.main.resources.srcDirs[0]}/webui"
exclude "*.gz"
//eachFile { println it.name }
}
task downloadRedoc(type: Download) {
src "https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js"
dest "${sourceSets.main.resources.srcDirs[0]}/public/redoc"
overwrite true
}
task var << {
sourceSets {
main {
println "java.srcDirs = ${java.srcDirs}"
println "resources.srcDirs = ${resources.srcDirs[0]}"
println "output.classesDir = ${output.classesDir}"
println "output.resourcesDir = ${output.resourcesDir}"
}
}
}
compileJava.dependsOn downloadRedoc
copyWebui.dependsOn removeWebui
compileJava.dependsOn copyWebui</pre>
- Create controller for APIs “WebRestController“
package com.test.practise.controller;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RefreshScope
@RestController
@RequestMapping("/api")
public class WebRestController {
@RequestMapping("test")
public String test(){
return "test api data";
}
}
- Create a view controller for enabling angular routes
<pre>package com.test.practise.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import springfox.documentation.annotations.ApiIgnore;
@Slf4j
@ApiIgnore
@Controller
public class ViewController {
@RequestMapping({ "/","/welcome" })
public String views() {
return "forward:/index.html";
}
}</pre>
This view controller will enable the spring to resolve these URL as angular URL.
Now we are good to go.Run the spring boot application using gradle bootRun.it will start the application.Once you will try to react the application from the browser, it will navigate you to google login first.
On Successful login, you will see the angular page.So till now we are pretty much done with authentication part. Now comes the routing part.
Now try to navigate to welcome page and setting page using above router.In this case, the welcome route will be successful but setting URL will fail, as we have not mentioned it in ViewController.
But when we try the routes which are below one, every route will work fine, as it is using angular routes.
For reference, you can check the GIT REPO with sample working code.