From b5ddc1c6d94365fa6c96131c97fe2b4c8c2541da Mon Sep 17 00:00:00 2001 From: ard333 Date: Sun, 13 May 2018 21:11:22 +0700 Subject: [PATCH] implement authentication and authorization --- pom.xml | 12 +- .../springbootwebfluxjjwt/entity/Message.java | 21 ++++ .../springbootwebfluxjjwt/entity/User.java | 103 ++++++++++++++++++ .../rest/AuthenticationREST.java | 49 +++++++++ .../rest/ResourceREST.java | 39 +++++++ .../security/AuthenticationManager.java | 62 +++++++++++ .../security/CORSFilter.java | 35 ++++++ .../security/JWTUtil.java | 75 +++++++++++++ .../security/PBKDF2Encoder.java | 48 ++++++++ .../security/SecurityContextRepository.java | 50 +++++++++ .../security/UserDetailsServiceImpl.java | 29 +++++ .../security/WebSecurityConfig.java | 39 +++++++ .../security/model/AuthRequest.java | 23 ++++ .../security/model/AuthResponse.java | 21 ++++ .../security/model/Role.java | 13 +++ .../service/UserService.java | 54 +++++++++ src/main/resources/application.properties | 3 + 17 files changed, 675 insertions(+), 1 deletion(-) create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/entity/Message.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/entity/User.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/rest/AuthenticationREST.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/rest/ResourceREST.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/AuthenticationManager.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/CORSFilter.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/JWTUtil.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/PBKDF2Encoder.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/SecurityContextRepository.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/UserDetailsServiceImpl.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/WebSecurityConfig.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthRequest.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthResponse.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/Role.java create mode 100644 src/main/java/id/web/ard/springbootwebfluxjjwt/service/UserService.java diff --git a/pom.xml b/pom.xml index 3722d8f..4b310cb 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.2.RELEASE + 2.0.0.RELEASE @@ -33,6 +33,16 @@ org.springframework.boot spring-boot-starter-webflux + + io.jsonwebtoken + jjwt + 0.7.0 + + + org.projectlombok + lombok + + org.springframework.boot diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/entity/Message.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/entity/Message.java new file mode 100644 index 0000000..3a71c76 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/entity/Message.java @@ -0,0 +1,21 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * + * @author ardiansyah + */ +@Data @NoArgsConstructor @AllArgsConstructor @ToString +public class Message { + + private String content; + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/entity/User.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/entity/User.java new file mode 100644 index 0000000..b2ddf7e --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/entity/User.java @@ -0,0 +1,103 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import id.web.ard.springbootwebfluxjjwt.security.model.Role; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * + * @author ardiansyah + */ +@ToString @AllArgsConstructor @NoArgsConstructor +public class User implements UserDetails { + + private String username; + + private String password; + + private Boolean enabled; + + private List roles; + + public User(String username) { + this.username = username; + } + + @Override + public String getUsername() { + return username; + } + + //============================== + @Override + public boolean isAccountNonExpired() { + return false; + } + + @Override + public boolean isAccountNonLocked() { + return false; + } + + @Override + public boolean isCredentialsNonExpired() { + return false; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public Collection getAuthorities() { + return this.roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList()); + } + //============================== + + @JsonIgnore + @Override + public String getPassword() { + return password; + } + + @JsonProperty + public void setPassword(String password) { + this.password = password; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + +} \ No newline at end of file diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/rest/AuthenticationREST.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/rest/AuthenticationREST.java new file mode 100644 index 0000000..41a8b7b --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/rest/AuthenticationREST.java @@ -0,0 +1,49 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.rest; + +import id.web.ard.springbootwebfluxjjwt.security.JWTUtil; +import id.web.ard.springbootwebfluxjjwt.security.PBKDF2Encoder; +import id.web.ard.springbootwebfluxjjwt.security.model.AuthRequest; +import id.web.ard.springbootwebfluxjjwt.security.model.AuthResponse; +import id.web.ard.springbootwebfluxjjwt.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +/** + * + * @author ardiansyah + */ +@RestController +public class AuthenticationREST { + + @Autowired + private JWTUtil jwtTokenUtil; + + @Autowired + private PBKDF2Encoder passwordEncoder; + + @Autowired + private UserService userRepository; + + @RequestMapping(value = "auth", method = RequestMethod.POST) + public Mono> auth(@RequestBody AuthRequest ar) { + return userRepository.findByUsername(ar.getUsername()).map((userDetails) -> { + if (passwordEncoder.encode(ar.getPassword()).equals(userDetails.getPassword())) { + return ResponseEntity.ok(new AuthResponse(jwtTokenUtil.generateToken(userDetails))); + } else { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + }); + } + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/rest/ResourceREST.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/rest/ResourceREST.java new file mode 100644 index 0000000..5f55b7f --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/rest/ResourceREST.java @@ -0,0 +1,39 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.rest; + +import id.web.ard.springbootwebfluxjjwt.entity.Message; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +/** + * + * @author ardiansyah + */ +@RestController +public class ResourceREST { + + @RequestMapping(value = "resource/user", method = RequestMethod.GET) + @PreAuthorize("hasRole('USER')") + public Mono> user() { + return Mono.just(ResponseEntity.ok(new Message("Content for user"))); + } + + @RequestMapping(value = "resource/admin", method = RequestMethod.GET) + @PreAuthorize("hasRole('ADMIN')") + public Mono> admin() { + return Mono.just(ResponseEntity.ok(new Message("Content for admin"))); + } + + @RequestMapping(value = "resource/user-or-admin", method = RequestMethod.GET) + @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public Mono> userOrAdmin() { + return Mono.just(ResponseEntity.ok(new Message("Content for user or admin"))); + } +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/AuthenticationManager.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/AuthenticationManager.java new file mode 100644 index 0000000..14460e8 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/AuthenticationManager.java @@ -0,0 +1,62 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security; + +import id.web.ard.springbootwebfluxjjwt.security.model.Role; +import io.jsonwebtoken.Claims; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; + +/** + * + * @author ardiansyah + */ +@Component +public class AuthenticationManager implements ReactiveAuthenticationManager { + + @Autowired + private JWTUtil jwtUtil; + + @Override + public Mono authenticate(Authentication authentication) { + String authToken = authentication.getCredentials().toString(); + + String username = null; + try { + username = jwtUtil.getUsernameFromToken(authToken); + } catch (Exception e) { + username = null; + } + if (username != null) { + Claims claims = jwtUtil.getAllClaimsFromToken(authToken); + if (jwtUtil.validateToken(authToken)) { + List rolesMap = claims.get("role", List.class); + List roles = new ArrayList<>(); + for (String rolemap : rolesMap) { + 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(); + } + } else { + return Mono.empty(); + } + } +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/CORSFilter.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/CORSFilter.java new file mode 100644 index 0000000..cbca046 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/CORSFilter.java @@ -0,0 +1,35 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security; + +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +/** + * + * @author ardiansyah + */ +@Component +public class CORSFilter implements WebFilter{ + + @Override + public Mono filter(ServerWebExchange swe, WebFilterChain wfc) { + + //CORS + swe.getResponse().getHeaders().add("Access-Control-Allow-Origin", "*"); + if (swe.getRequest().getHeaders().get("Access-Control-Request-Method") != null && "OPTIONS".equalsIgnoreCase(swe.getRequest().getMethod().toString())) { + swe.getResponse().getHeaders().add("Access-Control-Allow-Headers", "Authorization"); + swe.getResponse().getHeaders().add("Access-Control-Allow-Headers", "Content-Type"); + swe.getResponse().getHeaders().add("Access-Control-Max-Age", "1"); + swe.getResponse().getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); + } + + return wfc.filter(swe); + } + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/JWTUtil.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/JWTUtil.java new file mode 100644 index 0000000..5206f77 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/JWTUtil.java @@ -0,0 +1,75 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security; + +import id.web.ard.springbootwebfluxjjwt.entity.User; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * + * @author ardiansyah + */ +@Component +public class JWTUtil implements Serializable { + + private static final long serialVersionUID = 1L; + + @Value("${springbootwebfluxjjwt.jjwt.secret}") + private String secret; + + @Value("${springbootwebfluxjjwt.jjwt.expiration}") + private String expirationTime; + + public Claims getAllClaimsFromToken(String token) { + return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); + } + + public String getUsernameFromToken(String token) { + return getAllClaimsFromToken(token).getSubject(); + } + + public Date getExpirationDateFromToken(String token) { + return getAllClaimsFromToken(token).getExpiration(); + } + + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + public String generateToken(User user) { + Map claims = new HashMap<>(); + claims.put("role", user.getRoles()); + claims.put("enable", user.getEnabled()); + return doGenerateToken(claims, user.getUsername()); + } + + private String doGenerateToken(Map claims, String username) { + Long expirationTimeLong = Long.parseLong(expirationTime); //in second + + final Date createdDate = new Date(); + final Date expirationDate = new Date(createdDate.getTime() + expirationTimeLong * 1000); + return Jwts.builder() + .setClaims(claims) + .setSubject(username) + .setIssuedAt(createdDate) + .setExpiration(expirationDate) + .signWith(SignatureAlgorithm.HS512, secret) + .compact(); + } + + public Boolean validateToken(String token) { + return !isTokenExpired(token); + } + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/PBKDF2Encoder.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/PBKDF2Encoder.java new file mode 100644 index 0000000..60df094 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/PBKDF2Encoder.java @@ -0,0 +1,48 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security; + +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +/** + * + * @author ardiansyah + */ +@Component +public class PBKDF2Encoder implements PasswordEncoder{ + + @Value("${springbootwebfluxjjwt.password.secret}") + private String secret; + + /** + * More info (https://www.owasp.org/index.php/Hashing_Java) + * @param cs password + * @return encoded password + */ + @Override + public String encode(CharSequence cs) { + try { + byte[] result = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") + .generateSecret(new PBEKeySpec(cs.toString().toCharArray(), secret.getBytes(), 33, 256)) + .getEncoded(); + return Base64.getEncoder().encodeToString(result); + } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public boolean matches(CharSequence cs, String string) { + return encode(cs).equals(string); + } + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/SecurityContextRepository.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/SecurityContextRepository.java new file mode 100644 index 0000000..a2bc194 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/SecurityContextRepository.java @@ -0,0 +1,50 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * + * @author ardiansyah + */ +@Component +public class SecurityContextRepository implements ServerSecurityContextRepository{ + + @Autowired + private AuthenticationManager authenticationManager; + + @Override + public Mono save(ServerWebExchange swe, SecurityContext sc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Mono load(ServerWebExchange swe) { + ServerHttpRequest request = swe.getRequest(); + String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); + + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String authToken = authHeader.substring(7); + Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken); + return this.authenticationManager.authenticate(auth).map((authentication) -> { + return new SecurityContextImpl(authentication); + }); + } else { + return Mono.empty(); + } + } + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/UserDetailsServiceImpl.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/UserDetailsServiceImpl.java new file mode 100644 index 0000000..8e809c9 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/UserDetailsServiceImpl.java @@ -0,0 +1,29 @@ +/* + * 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 findByUsername(String username) { + return userRepository.findUserDetailsByUsername(username); + } + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/WebSecurityConfig.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/WebSecurityConfig.java new file mode 100644 index 0000000..6051932 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/WebSecurityConfig.java @@ -0,0 +1,39 @@ +package id.web.ard.springbootwebfluxjjwt.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; + +/** + * + * @author ardiansyah + */ +@EnableWebFluxSecurity +@EnableReactiveMethodSecurity +public class WebSecurityConfig { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private SecurityContextRepository securityContextRepository; + + @Bean + public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) { + return http.csrf().disable() + .formLogin().disable() + .httpBasic().disable() + .authenticationManager(authenticationManager) + .securityContextRepository(securityContextRepository) + //.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.HTTP_BASIC) + .authorizeExchange() + .pathMatchers("/auth").permitAll() + .anyExchange().authenticated() + .and().build(); + + } +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthRequest.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthRequest.java new file mode 100644 index 0000000..38bc5d8 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthRequest.java @@ -0,0 +1,23 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * + * @author ardiansyah + */ +@Data @NoArgsConstructor @AllArgsConstructor @ToString +public class AuthRequest { + + private String username; + + private String password; + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthResponse.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthResponse.java new file mode 100644 index 0000000..3314e57 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/AuthResponse.java @@ -0,0 +1,21 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * + * @author ardiansyah + */ +@Data @NoArgsConstructor @AllArgsConstructor @ToString +public class AuthResponse { + + private String token; + +} diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/Role.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/Role.java new file mode 100644 index 0000000..f5823a6 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/security/model/Role.java @@ -0,0 +1,13 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.security.model; + +/** + * + * @author ardiansyah + */ +public enum Role { + ROLE_USER, ROLE_ADMIN +} \ No newline at end of file diff --git a/src/main/java/id/web/ard/springbootwebfluxjjwt/service/UserService.java b/src/main/java/id/web/ard/springbootwebfluxjjwt/service/UserService.java new file mode 100644 index 0000000..c76f049 --- /dev/null +++ b/src/main/java/id/web/ard/springbootwebfluxjjwt/service/UserService.java @@ -0,0 +1,54 @@ +/* + * Ardiansyah | http://ard.web.id + * + */ +package id.web.ard.springbootwebfluxjjwt.service; + +import id.web.ard.springbootwebfluxjjwt.entity.User; +import id.web.ard.springbootwebfluxjjwt.security.model.Role; +import java.util.Arrays; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +/** + * + * @author ardiansyah + */ +@Service +public class UserService { + + // this is just an example, you can load the user from the database from the repository + + //username:passwowrd -> user:user + private final String userUsername = "user";// password: user + private final User user = new User(userUsername, "cBrlgyL2GI2GINuLUUwgojITuIufFycpLG4490dhGtY=", true, Arrays.asList(Role.ROLE_USER)); + + //username:passwowrd -> admin:admin + private final String adminUsername = "admin";// password: admin + private final User admin = new User(adminUsername, "dQNjUIMorJb8Ubj2+wVGYp6eAeYkdekqAcnYp+aRq5w=", true, Arrays.asList(Role.ROLE_ADMIN)); + + + public Mono findUserDetailsByUsername(String username) { + if (username.equals(userUsername)) { + return Mono.just(user); + } else if (username.equals(adminUsername)) { + return Mono.just(admin); + } else { + return Mono.empty(); + } + } + + public Mono findByUsername(String username) { + if (username.equals(userUsername)) { + return Mono.just(user); + } else if (username.equals(adminUsername)) { + return Mono.just(admin); + } else { + return Mono.empty(); + } + } + + + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29..21614dd 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,3 @@ +springbootwebfluxjjwt.password.secret=mysecret +springbootwebfluxjjwt.jjwt.secret=mysecret +springbootwebfluxjjwt.jjwt.expiration=28800 \ No newline at end of file