refactoring

This commit is contained in:
ard333
2018-07-22 21:36:08 +07:00
parent 636264098c
commit 74b2cb9acd
19 changed files with 73 additions and 173 deletions

View File

@@ -1 +1,5 @@
# spring-boot-webflux-jjwt # spring-boot-webflux-jjwt
Example Spring Boot and WebFlux (Reactive Web) with Spring Security and JWT for token Authentication and Authorization
### Documantation
You can se my Medium story, [Authentication and Authorization Using JWT on Spring Webflux](https://medium.com/@ard333/29b81f813e78)

View File

@@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>id.web.ard</groupId> <groupId>com.ard333</groupId>
<artifactId>spring-boot-webflux-jjwt</artifactId> <artifactId>spring-boot-webflux-jjwt</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@@ -1,4 +1,4 @@
package id.web.ard.springbootwebfluxjjwt; package com.ard333.springbootwebfluxjjwt;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -1,8 +1,4 @@
/* package com.ard333.springbootwebfluxjjwt.model;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -11,7 +7,7 @@ import lombok.ToString;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Data @NoArgsConstructor @AllArgsConstructor @ToString @Data @NoArgsConstructor @AllArgsConstructor @ToString
public class Message { public class Message {

View File

@@ -1,26 +1,23 @@
/* package com.ard333.springbootwebfluxjjwt.model;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.model;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import id.web.ard.springbootwebfluxjjwt.security.model.Role; import com.ard333.springbootwebfluxjjwt.security.model.Role;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString; import lombok.ToString;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@ToString @AllArgsConstructor @NoArgsConstructor @ToString @AllArgsConstructor @NoArgsConstructor
public class User implements UserDetails { public class User implements UserDetails {
@@ -29,20 +26,21 @@ public class User implements UserDetails {
private String password; private String password;
@Getter @Setter
private Boolean enabled; private Boolean enabled;
@Getter @Setter
private List<Role> roles; private List<Role> roles;
public User(String username) {
this.username = username;
}
@Override @Override
public String getUsername() { public String getUsername() {
return username; return username;
} }
public void setUsername(String username) {
this.username = username;
}
//==============================
@Override @Override
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return false; return false;
@@ -63,15 +61,10 @@ public class User implements UserDetails {
return this.enabled; return this.enabled;
} }
public void setUsername(String username) {
this.username = username;
}
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList()); return this.roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList());
} }
//==============================
@JsonIgnore @JsonIgnore
@Override @Override
@@ -84,20 +77,4 @@ public class User implements UserDetails {
this.password = password; this.password = password;
} }
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
} }

View File

@@ -1,18 +1,13 @@
/* package com.ard333.springbootwebfluxjjwt.rest;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.rest;
import id.web.ard.springbootwebfluxjjwt.security.JWTUtil; import com.ard333.springbootwebfluxjjwt.security.JWTUtil;
import id.web.ard.springbootwebfluxjjwt.security.PBKDF2Encoder; import com.ard333.springbootwebfluxjjwt.security.PBKDF2Encoder;
import id.web.ard.springbootwebfluxjjwt.security.model.AuthRequest; import com.ard333.springbootwebfluxjjwt.security.model.AuthRequest;
import id.web.ard.springbootwebfluxjjwt.security.model.AuthResponse; import com.ard333.springbootwebfluxjjwt.security.model.AuthResponse;
import id.web.ard.springbootwebfluxjjwt.service.UserService; import com.ard333.springbootwebfluxjjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@@ -21,13 +16,13 @@ import reactor.core.publisher.Mono;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@RestController @RestController
public class AuthenticationREST { public class AuthenticationREST {
@Autowired @Autowired
private JWTUtil jwtTokenUtil; private JWTUtil jwtUtil;
@Autowired @Autowired
private PBKDF2Encoder passwordEncoder; private PBKDF2Encoder passwordEncoder;
@@ -39,7 +34,7 @@ public class AuthenticationREST {
public Mono<ResponseEntity<AuthResponse>> auth(@RequestBody AuthRequest ar) { public Mono<ResponseEntity<AuthResponse>> auth(@RequestBody AuthRequest ar) {
return userRepository.findByUsername(ar.getUsername()).map((userDetails) -> { return userRepository.findByUsername(ar.getUsername()).map((userDetails) -> {
if (passwordEncoder.encode(ar.getPassword()).equals(userDetails.getPassword())) { if (passwordEncoder.encode(ar.getPassword()).equals(userDetails.getPassword())) {
return ResponseEntity.ok(new AuthResponse(jwtTokenUtil.generateToken(userDetails))); return ResponseEntity.ok(new AuthResponse(jwtUtil.generateToken(userDetails)));
} else { } else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} }

View File

@@ -2,9 +2,9 @@
* Ardiansyah | http://ard.web.id * Ardiansyah | http://ard.web.id
* *
*/ */
package id.web.ard.springbootwebfluxjjwt.rest; package com.ard333.springbootwebfluxjjwt.rest;
import id.web.ard.springbootwebfluxjjwt.model.Message; import com.ard333.springbootwebfluxjjwt.model.Message;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;

View File

@@ -1,10 +1,6 @@
/* package com.ard333.springbootwebfluxjjwt.security;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security;
import id.web.ard.springbootwebfluxjjwt.security.model.Role; import com.ard333.springbootwebfluxjjwt.security.model.Role;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager;
@@ -16,11 +12,10 @@ import reactor.core.publisher.Mono;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Component @Component
public class AuthenticationManager implements ReactiveAuthenticationManager { public class AuthenticationManager implements ReactiveAuthenticationManager {
@@ -32,29 +27,25 @@ public class AuthenticationManager implements ReactiveAuthenticationManager {
public Mono<Authentication> authenticate(Authentication authentication) { public Mono<Authentication> authenticate(Authentication authentication) {
String authToken = authentication.getCredentials().toString(); String authToken = authentication.getCredentials().toString();
String username = null; String username;
try { try {
username = jwtUtil.getUsernameFromToken(authToken); username = jwtUtil.getUsernameFromToken(authToken);
} catch (Exception e) { } catch (Exception e) {
username = null; username = null;
} }
if (username != null) { if (username != null && jwtUtil.validateToken(authToken)) {
Claims claims = jwtUtil.getAllClaimsFromToken(authToken); Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
if (jwtUtil.validateToken(authToken)) { List<String> rolesMap = claims.get("role", List.class);
List<String> rolesMap = claims.get("role", List.class); List<Role> roles = new ArrayList<>();
List<Role> roles = new ArrayList<>(); for (String rolemap : rolesMap) {
for (String rolemap : rolesMap) { roles.add(Role.valueOf(rolemap));
roles.add(Role.valueOf(rolemap));
}
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
username,
null,
roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
);
return Mono.just(auth);
} else {
return Mono.empty();
} }
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
username,
null,
roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
);
return Mono.just(auth);
} else { } else {
return Mono.empty(); return Mono.empty();
} }

View File

@@ -1,8 +1,4 @@
/* package com.ard333.springbootwebfluxjjwt.security;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry; import org.springframework.web.reactive.config.CorsRegistry;
@@ -11,7 +7,7 @@ import org.springframework.web.reactive.config.WebFluxConfigurer;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Configuration @Configuration
@EnableWebFlux @EnableWebFlux

View File

@@ -1,10 +1,6 @@
/* package com.ard333.springbootwebfluxjjwt.security;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security;
import id.web.ard.springbootwebfluxjjwt.model.User; import com.ard333.springbootwebfluxjjwt.model.User;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@@ -17,7 +13,7 @@ import org.springframework.stereotype.Component;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Component @Component
public class JWTUtil implements Serializable { public class JWTUtil implements Serializable {

View File

@@ -1,8 +1,4 @@
/* package com.ard333.springbootwebfluxjjwt.security;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
@@ -15,7 +11,7 @@ import org.springframework.stereotype.Component;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Component @Component
public class PBKDF2Encoder implements PasswordEncoder{ public class PBKDF2Encoder implements PasswordEncoder{

View File

@@ -1,8 +1,4 @@
/* package com.ard333.springbootwebfluxjjwt.security;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@@ -18,7 +14,7 @@ import reactor.core.publisher.Mono;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Component @Component
public class SecurityContextRepository implements ServerSecurityContextRepository{ public class SecurityContextRepository implements ServerSecurityContextRepository{

View File

@@ -1,4 +1,4 @@
package id.web.ard.springbootwebfluxjjwt.security; package com.ard333.springbootwebfluxjjwt.security;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -10,7 +10,7 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@EnableWebFluxSecurity @EnableWebFluxSecurity
@EnableReactiveMethodSecurity @EnableReactiveMethodSecurity

View File

@@ -1,8 +1,4 @@
/* package com.ard333.springbootwebfluxjjwt.security.model;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -11,7 +7,7 @@ import lombok.ToString;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Data @NoArgsConstructor @AllArgsConstructor @ToString @Data @NoArgsConstructor @AllArgsConstructor @ToString
public class AuthRequest { public class AuthRequest {

View File

@@ -1,8 +1,4 @@
/* package com.ard333.springbootwebfluxjjwt.security.model;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -11,7 +7,7 @@ import lombok.ToString;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Data @NoArgsConstructor @AllArgsConstructor @ToString @Data @NoArgsConstructor @AllArgsConstructor @ToString
public class AuthResponse { public class AuthResponse {

View File

@@ -0,0 +1,9 @@
package com.ard333.springbootwebfluxjjwt.security.model;
/**
*
* @author ard333
*/
public enum Role {
ROLE_USER, ROLE_ADMIN
}

View File

@@ -1,11 +1,7 @@
/* package com.ard333.springbootwebfluxjjwt.service;
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.service;
import id.web.ard.springbootwebfluxjjwt.model.User; import com.ard333.springbootwebfluxjjwt.model.User;
import id.web.ard.springbootwebfluxjjwt.security.model.Role; import com.ard333.springbootwebfluxjjwt.security.model.Role;
import java.util.Arrays; import java.util.Arrays;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -13,7 +9,7 @@ import reactor.core.publisher.Mono;
/** /**
* *
* @author ardiansyah * @author ard333
*/ */
@Service @Service
public class UserService { public class UserService {
@@ -49,6 +45,4 @@ public class UserService {
} }
} }
} }

View File

@@ -1,29 +0,0 @@
/*
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security;
import id.web.ard.springbootwebfluxjjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
*
* @author ardiansyah
*/
@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {
@Autowired
private UserService userRepository;
@Override
public Mono<UserDetails> findByUsername(String username) {
return userRepository.findUserDetailsByUsername(username);
}
}

View File

@@ -1,13 +0,0 @@
/*
* Ardiansyah | http://ard.web.id
*
*/
package id.web.ard.springbootwebfluxjjwt.security.model;
/**
*
* @author ardiansyah
*/
public enum Role {
ROLE_USER, ROLE_ADMIN
}