Spring Angular Secruity Integration 20 Jan 21
Spring Angular Secruity Integration 20 Jan 21
server.servlet.context-path=/empapp
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/kr_jdbc?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
spring.jpa.show-sql=true
@Repository
public interface EmpRepo extends CrudRepository<Employee, Integer>{
@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmpRepo empRepo;
@Override
public List<Employee> getAll() {
return (List<Employee>) empRepo.findAll();
}
@Override
public Employee getEmployeeById(int id) {
return empRepo.findById(id).orElseThrow(EmployeeNotFoundException:: new);
}
@Override
public Employee save(Employee emp) {
return empRepo.save(emp);
}
@Override
public Employee delete(int empId) {
Employee employeeToDelete=getEmployeeById(empId);
empRepo.delete(employeeToDelete);
return employeeToDelete;
}
@Override
public Employee update(int empId, Employee emp) {
Employee employeeToUpdate=getEmployeeById(empId);
employeeToUpdate.setName(emp.getName());
employeeToUpdate.setAge(emp.getAge());
return empRepo.save(employeeToUpdate);
}
}
@RestController
public class EmpRestController {
@Autowired
private EmployeeService employeeService;
@GetMapping(path="employee", produces=MediaType.APPLICATION_JSON_VALUE)
public List<Employee> allEmployees(){
return employeeService.getAll();
}
@GetMapping(path="employee/{id}",produces=MediaType.APPLICATION_JSON_VALUE )
public Employee getEmployeeById(@PathVariable(name="id")int id){
return employeeService.getEmployeeById(id);
}
@PostMapping(path="employee",produces=MediaType.APPLICATION_JSON_VALUE,
consumes=MediaType.APPLICATION_JSON_VALUE )
public Employee addEmployee(@RequestBody Employee employee){
return employeeService.save(employee);
}
@PutMapping(path="employee/{id}",produces=MediaType.APPLICATION_JSON_VALUE,
consumes=MediaType.APPLICATION_JSON_VALUE )
public Employee updateEmployee(@PathVariable(name="id") int id, @RequestBody
Employee emp){
return employeeService.update(id, emp);
@DeleteMapping(path="employee/{id}",produces=MediaType.APPLICATION_JSON_VALUE)
public Employee deleteEmplloyee(@PathVariable(name="id") int id){
return employeeService.delete(id);
}
}
@SpringBootApplication
public class DemoApplication implements CommandLineRunner{
@Autowired
private EmployeeService empService;
@Override
public void run(String... args) throws Exception {
System.out.println("rec are saved...");
empService.save(new Employee("raj", 33));
empService.save(new Employee("ekta", 30));
empService.save(new Employee("gunika", 10));
empService.save(new Employee("keshav", 5));
}
or
@Component
public class CORSFilter implements Filter {
1. configure bootstrap:
install bootstrap:
----------------
sudo npm install bootstrap --save
@import "~bootstrap/dist/css/bootstrap.min.css"
check if working:
------------------
<router-outlet></router-outlet>
alternative ways:
----------------
angular.json:
------------
"styles": [
"./node_modules/bootstrap/dist/css/bootstrap-grid.css",
"src/styles.css"
],
angular.cli.json
---------------------
npm install bootstrap@4.0.0-alpha.6 --save
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
"../node_modules/jquery/dist/jquery.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js"
employees: Employee[];
constructor() { }
ngOnInit() {
this.employees=[
{
"id":1,
"name":"rajeev",
"age":40
},
{
"id":2,
"name":"ekta",
"age":40
},
{
"id":3,
"name":"gunika",
"age":15
}
];
}
<div class="text-center">
<app-employee></app-employee>
</div>
create service to fetch with rest endpoint:
--------------------------------
ng g s employee
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],
install rxjs:
--------------
employee service:
--------------------
http://localhost:8080/empapp/api/employee
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
private baseURL="http://localhost:8080/empapp/employee";
constructor(private httpClient: HttpClient) { }
getEmployeesList(): Observable<Employee[]>{
return this.httpClient.get<Employee[]>(`${this.baseURL}`);
}
}
employee component:
--------------------
import { Component, OnInit } from '@angular/core';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
employees: Employee[];
ngOnInit() {
this.getEmployees();
}
private getEmployees(){
this.employeeService.getEmployeesList().subscribe(data=>{
this.employees=data;
});
}
<div class="text-center">
<router-outlet></router-outlet>
</div>
<div class="text-center">
<router-outlet></router-outlet>
</div>
add to style.css
------------------
@import "~bootstrap/dist/css/bootstrap.min.css";
.footer {
position: absolute;
bottom: 0;
width:100%;
height: 40px;
background-color: blue;
text-align: center;
color: white;
}
update app.component.html
------------------------
ngOnInit() {
}
onSubmit(){
console.log(this.employee);
}
}
<div class="form-group">
<label> Name</label>
<input type="text" class ="form-control" id = "name"
[(ngModel)] = "employee.name" name = "name">
</div>
<div class="form-group">
<label> Age </label>
<input type="text" class ="form-control" id = "age"
[(ngModel)] = "employee.age" name = "age">
</div>
<button class = "btn btn-success" type ="submit">Submit</button>
</form>
</div>
Note:
=> Error:Can't bind to 'ngModel' since it isn't a known property of 'input'. (
imports: [
BrowserModule,
FormsModule
]
@Component({
selector: 'app-create-employee',
templateUrl: './create-employee.component.html',
styleUrls: ['./create-employee.component.css']
})
ngOnInit(): void {
}
saveEmployee(){
this.employeeService.createEmployee(this.employee).subscribe( data =>{
console.log(data);
this.goToEmployeeList();
},
error => console.log(error));
}
goToEmployeeList(){
this.router.navigate(['/employees']);
}
onSubmit(){
console.log(this.employee);
this.saveEmployee();
}
}
ng g c update-employee
];
//.......
employees: Employee[];
ngOnInit() {
this.getEmployees();
}
private getEmployees(){
this.employeeService.getEmployeesList().subscribe(data=>{
this.employees=data;
});
}
updateEmployee(id: number){
console.log(`-----------`)
this.router.navigate(['update-employee', id]);
}
@Component({
selector: 'app-update-employee',
templateUrl: './update-employee.component.html',
styleUrls: ['./update-employee.component.css']
})
export class UpdateEmployeeComponent implements OnInit {
id: number;
employee: Employee = new Employee();
constructor(private employeeService: EmployeeService,private route: ActivatedRoute) { }
ngOnInit(): void {
this.id=this.route.snapshot.params['id'];
this.employeeService.getEmployeeById(this.id).subscribe(data=>{
this.employee=data;
}, error=>console.log(error))
}
Now code for update and route back to showing all records:
----------------------------------------------------------
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
@Component({
selector: 'app-update-employee',
templateUrl: './update-employee.component.html',
styleUrls: ['./update-employee.component.css']
})
ngOnInit(): void {
this.id=this.route.snapshot.params['id'];
this.employeeService.getEmployeeById(this.id).subscribe(data=>{
this.employee=data;
}, error=>console.log(error))
}
onSubmit(){
this.employeeService.updateEmployee(this.id, this.employee)
.subscribe(data=> {
this.goToEmployeeList();
}, error=> console.log(error))
}
goToEmployeeList(){
this.router.navigate(['/employees']);
}
}
Delete employee:
-------------------
//...........
deleteEmployee(id: number): Observable<Object>{
return this.httpClient.delete(`${this.baseURL}/${id}`);
}
}
//....
deleteEmployee(id: number){
this.employeeService.deleteEmployee(id).subscribe(data=>{
this.getEmployees();
console.log(data);
})
}
employeeDetails(id: number){
this.router.navigate(['employee-details', id]);
}
}
ng g c employee-details
];
EmployeeDetailsComponent code:
--------------------------------
import { ActivatedRoute, Router } from '@angular/router';
import { Employee } from '../employee';
import { EmployeeService } from '../employee.service';
id: number
employee: Employee=new Employee();
ngOnInit(): void {
this.id = this.route.snapshot.params['id'];
//....
employeeDetails(id: number){
this.router.navigate(['employee-details', id]);
}
}
Employee service:
---------------
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
getEmployeesList(): Observable<Employee[]>{
return this.httpClient.get<Employee[]>(`${this.baseURL}`);
}
=> Create a new authentication service where we check if the user name and password is
correct then set it in session storage.
=> Using sessionStorage properties we can save key/value pairs in a web browser.
The sessionStorage object stores data for only one session .
So the data gets deleted if the browser is closed
ng g s authentication
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
constructor() { }
authenticate(username, password) {
if (username === "raj" && password === "raj123") {
sessionStorage.setItem('username', username)
return true;
} else {
return false;
}
}
isUserLoggedIn() {
let user = sessionStorage.getItem('username')
console.log(!(user === null))
return !(user === null)
}
logOut() {
sessionStorage.removeItem('username')
}
}
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
username = 'raj'
password = ''
invalidLogin = false
ngOnInit() {
}
checkLogin() {
if (this.loginservice.authenticate(this.username, this.password)
){
this.router.navigate([''])
this.invalidLogin = false
} else
this.invalidLogin = true
}
}
from:
--------
<div class="container">
<div>
User Name : <input type="text" name="username" [(ngModel)]="username">
Password : <input type="password" name="password" [(ngModel)]="password">
</div>
<button (click)=checkLogin() class="btn btn-success">
Login
</button>
</div>
Add the login path to the routing module.
-----------------------------
@Component({
selector: 'app-logout',
templateUrl: './logout.component.html',
styleUrls: ['./logout.component.css']
})
export class LogoutComponent implements OnInit {
constructor(
private authentocationService: AuthenticationService,
private router: Router) {
ngOnInit() {
this.authentocationService.logOut();
this.router.navigate(['login']);
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
constructor(private loginService:AuthenticationService) { }
title = 'empclient';
ngOnInit() {
}
}
=> But what will happen if the user directly tries to access a page without login.
For example if a user directly navigates to localhost:4200 He will be able to view the page.
=> So we should first check if the user is logged in and only then allow the user to view the
page. We achive this using the CanActivate interface.
this.router.navigate(['login']);
return false;
}
}
Modify the app.routing.ts to activate route only if the user is logged in using the above
AuthGaurdService.
--------------------------------------------------------------------------------------------
Authorization header which has the word Basic followed by a space and base 64 encoded string
username:password
@CrossOrigin(origins = "http://localhost:4200")
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().
authorizeRequests().antMatchers(HttpMethod.OPTIONS,
"/**").permitAll().anyRequest().authenticated()
.and().httpBasic();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("raj").password("{noop}raj123").roles("USER");
}
}
@GetMapping(produces = "application/json")
@RequestMapping({ "/validateLogin" })
public AuthResponse validateLogin() {
return new AuthResponse("User successfully authenticated");
}
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
private baseURL="http://localhost:8080/empapp/employee";
constructor(private httpClient: HttpClient) { }
getEmployeesList(): Observable<Employee[]>{
let username='raj'
let password='raj123'
const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' + password) });
return this.httpClient.get<Employee[]>(`${this.baseURL}`,{headers});
}
@Injectable({
providedIn: 'root'
})
export class HttpClientService {
constructor(
private httpClient:HttpClient
){
}
getEmployees()
{
let username='raj'
let password='raj123'
const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' + password) });
return this.httpClient.get<Employee[]>('http://localhost:8080/employees',{headers});
}
public deleteEmployee(employee) {
let username='raj'
let password='raj123'
const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' + password) });
return this.httpClient.delete<Employee>("http://localhost:8080/employees" + "/"+
employee.empId,{headers});
}
public createEmployee(employee) {
let username='raj'
let password='raj123'
const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' + password) });
return this.httpClient.post<Employee>("http://localhost:8080/employees", employee,{headers});
}
Previously the authentication.service.ts used to check the credentials against hardcoded values. Now
we will make a REST call using Basic Auth header. Only if the User object is returned will be login
be successful.
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
constructor(private httpClient:HttpClient) { }
authenticate(username, password) {
const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' + password) });
return this.httpClient.get<AuthResponse>('http://localhost:8080/empapp/validateLogin',
{headers}).pipe(
map(
userData => {
sessionStorage.setItem('username',username);
return userData;
}
)
);
}
isUserLoggedIn() {
let user = sessionStorage.getItem('username')
console.log(!(user === null))
return !(user === null)
}
logOut() {
sessionStorage.removeItem('username')
}
}
In the login.service.ts we check if the valid user is returned by the authentication service. If yes then
login is successful and the user is forwarded to the employee page.
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
username = ''
password = ''
invalidLogin = false
ngOnInit() {
}
checkLogin() {
(this.loginservice.authenticate(this.username, this.password).subscribe(
data => {
this.router.navigate([''])
this.invalidLogin = false
},
error => {
this.invalidLogin = true
}
)
);
We had seen we had to duplicate the code for adding Basic Auth Headers to the HTTPRequest
before making HTTP calls.
In the authentication.service.ts if the authentication for the user entered username and password is
successful, we will be saving the basicAuth string which we are adding the
Authorization Header for basic Authenication in the session.
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
export class AuthResponse{
constructor(public status:string) {}
}
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
constructor(private httpClient:HttpClient) { }
authenticate(username, password) {
const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' + password) });
return this.httpClient.get<AuthResponse>('http://localhost:8080/empapp/validateLogin',
{headers}).pipe(
map(
userData => {
sessionStorage.setItem('username', username);
let authString = 'Basic ' + btoa(username + ':' + password);
sessionStorage.setItem('basicauth', authString);
return userData;
}
)
);
}
isUserLoggedIn() {
let user = sessionStorage.getItem('username')
console.log(!(user === null))
return !(user === null)
}
logOut() {
sessionStorage.removeItem('username')
}
}
HttpInterceptor service:
------------------------
Next we will be creating a new HttpInterceptor service called BasicAuthInterceptor Service. This
service will check if the session has valid username and basicAuth String, then it will update the
headers of all outgoing HTTP requests. We implement the interceptor by extending the
HttpInterceptor.
ng g service BasicAuthHtppInterceptor
@Injectable({
providedIn: 'root'
})
export class BasicAuthHtppInterceptorService implements HttpInterceptor {
constructor() { }
return next.handle(req);
}
}
Now we will register the created HTTPInterceptor using the app.module.ts by updating it in the
provider section.
providers: [
{
provide:HTTP_INTERCEPTORS, useClass:BasicAuthHtppInterceptorService, multi:true
}
]
Finally we will remove the hardcoded basic auth logic from the Http client service.
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
private baseURL="http://localhost:8080/empapp/employee";
constructor(private httpClient: HttpClient) { }
getEmployeesList(): Observable<Employee[]>{
// let username='raj'
// let password='raj123'
// const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa(username + ':' +
password) });
// return this.httpClient.get<Employee[]>(`${this.baseURL}`,{headers});
return this.httpClient.get<Employee[]>(`${this.baseURL}`);
}