refactoring
This commit is contained in:
@@ -3,13 +3,10 @@ package com.ard333.springbootwebfluxjjwt.model;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
/**
|
@Data
|
||||||
*
|
@NoArgsConstructor
|
||||||
* @author ard333
|
@AllArgsConstructor
|
||||||
*/
|
|
||||||
@Data @NoArgsConstructor @AllArgsConstructor @ToString
|
|
||||||
public class Message {
|
public class Message {
|
||||||
private String content;
|
private String content;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.ard333.springbootwebfluxjjwt.model;
|
package com.ard333.springbootwebfluxjjwt.model;
|
||||||
|
|
||||||
|
import com.ard333.springbootwebfluxjjwt.model.security.Role;
|
||||||
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 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;
|
||||||
@@ -15,24 +16,27 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
/**
|
@ToString
|
||||||
*
|
@NoArgsConstructor
|
||||||
* @author ard333
|
@AllArgsConstructor
|
||||||
*/
|
|
||||||
@ToString @AllArgsConstructor @NoArgsConstructor
|
|
||||||
public class User implements UserDetails {
|
public class User implements UserDetails {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
private Boolean enabled;
|
private Boolean enabled;
|
||||||
|
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
private List<Role> roles;
|
private List<Role> roles;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
@@ -67,6 +71,7 @@ public class User implements UserDetails {
|
|||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public void setPassword(String password) {
|
public void setPassword(String password) {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ard333.springbootwebfluxjjwt.model.security;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AuthRequest {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.ard333.springbootwebfluxjjwt.model.security;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AuthResponse {
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.ard333.springbootwebfluxjjwt.model.security;
|
||||||
|
|
||||||
|
public enum Role {
|
||||||
|
ROLE_USER, ROLE_ADMIN
|
||||||
|
}
|
||||||
@@ -1,43 +1,33 @@
|
|||||||
package com.ard333.springbootwebfluxjjwt.rest;
|
package com.ard333.springbootwebfluxjjwt.rest;
|
||||||
|
|
||||||
|
import com.ard333.springbootwebfluxjjwt.model.security.AuthRequest;
|
||||||
|
import com.ard333.springbootwebfluxjjwt.model.security.AuthResponse;
|
||||||
import com.ard333.springbootwebfluxjjwt.security.JWTUtil;
|
import com.ard333.springbootwebfluxjjwt.security.JWTUtil;
|
||||||
import com.ard333.springbootwebfluxjjwt.security.PBKDF2Encoder;
|
import com.ard333.springbootwebfluxjjwt.security.PBKDF2Encoder;
|
||||||
import com.ard333.springbootwebfluxjjwt.security.model.AuthRequest;
|
|
||||||
import com.ard333.springbootwebfluxjjwt.security.model.AuthResponse;
|
|
||||||
import com.ard333.springbootwebfluxjjwt.service.UserService;
|
import com.ard333.springbootwebfluxjjwt.service.UserService;
|
||||||
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.web.bind.annotation.PostMapping;
|
||||||
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.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
/**
|
@AllArgsConstructor
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
public class AuthenticationREST {
|
public class AuthenticationREST {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JWTUtil jwtUtil;
|
private JWTUtil jwtUtil;
|
||||||
@Autowired
|
|
||||||
private PBKDF2Encoder passwordEncoder;
|
private PBKDF2Encoder passwordEncoder;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@RequestMapping(value = "/login", method = RequestMethod.POST)
|
@PostMapping("/login")
|
||||||
public Mono<ResponseEntity<?>> login(@RequestBody AuthRequest ar) {
|
public Mono<ResponseEntity<AuthResponse>> login(@RequestBody AuthRequest ar) {
|
||||||
return userService.findByUsername(ar.getUsername()).map((userDetails) -> {
|
return userService.findByUsername(ar.getUsername())
|
||||||
if (passwordEncoder.encode(ar.getPassword()).equals(userDetails.getPassword())) {
|
.filter(userDetails -> passwordEncoder.encode(ar.getPassword()).equals(userDetails.getPassword()))
|
||||||
return ResponseEntity.ok(new AuthResponse(jwtUtil.generateToken(userDetails)));
|
.map(userDetails -> ResponseEntity.ok(new AuthResponse(jwtUtil.generateToken(userDetails))))
|
||||||
} else {
|
.switchIfEmpty(Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()));
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
|
||||||
}
|
|
||||||
}).defaultIfEmpty(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,30 +3,28 @@ package com.ard333.springbootwebfluxjjwt.rest;
|
|||||||
import com.ard333.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.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author ardiansyah
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ResourceREST {
|
public class ResourceREST {
|
||||||
@RequestMapping(value = "/resource/user", method = RequestMethod.GET)
|
|
||||||
|
@GetMapping("/resource/user")
|
||||||
@PreAuthorize("hasRole('USER')")
|
@PreAuthorize("hasRole('USER')")
|
||||||
public Mono<ResponseEntity<?>> user() {
|
public Mono<ResponseEntity<Message>> user() {
|
||||||
return Mono.just(ResponseEntity.ok(new Message("Content for user")));
|
return Mono.just(ResponseEntity.ok(new Message("Content for user")));
|
||||||
}
|
}
|
||||||
@RequestMapping(value = "/resource/admin", method = RequestMethod.GET)
|
|
||||||
|
@GetMapping("/resource/admin")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
public Mono<ResponseEntity<?>> admin() {
|
public Mono<ResponseEntity<Message>> admin() {
|
||||||
return Mono.just(ResponseEntity.ok(new Message("Content for admin")));
|
return Mono.just(ResponseEntity.ok(new Message("Content for admin")));
|
||||||
}
|
}
|
||||||
@RequestMapping(value = "/resource/user-or-admin", method = RequestMethod.GET)
|
|
||||||
|
@GetMapping("/resource/user-or-admin")
|
||||||
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
|
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
|
||||||
public Mono<ResponseEntity<?>> userOrAdmin() {
|
public Mono<ResponseEntity<Message>> userOrAdmin() {
|
||||||
return Mono.just(ResponseEntity.ok(new Message("Content for user or admin")));
|
return Mono.just(ResponseEntity.ok(new Message("Content for user or admin")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,39 @@
|
|||||||
package com.ard333.springbootwebfluxjjwt.security;
|
package com.ard333.springbootwebfluxjjwt.security;
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@Component
|
@Component
|
||||||
|
@AllArgsConstructor
|
||||||
public class AuthenticationManager implements ReactiveAuthenticationManager {
|
public class AuthenticationManager implements ReactiveAuthenticationManager {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JWTUtil jwtUtil;
|
private JWTUtil jwtUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||||
String authToken = authentication.getCredentials().toString();
|
String authToken = authentication.getCredentials().toString();
|
||||||
try {
|
String username = jwtUtil.getUsernameFromToken(authToken);
|
||||||
String username = jwtUtil.getUsernameFromToken(authToken);
|
return Mono.just(jwtUtil.validateToken(authToken))
|
||||||
if (!jwtUtil.validateToken(authToken)) {
|
.filter(valid -> valid)
|
||||||
return Mono.empty();
|
.switchIfEmpty(Mono.empty())
|
||||||
}
|
.map(valid -> {
|
||||||
Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
|
Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
|
||||||
List<String> rolesMap = claims.get("role", List.class);
|
List<String> rolesMap = claims.get("role", List.class);
|
||||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
return new UsernamePasswordAuthenticationToken(
|
||||||
for (String rolemap : rolesMap) {
|
username,
|
||||||
authorities.add(new SimpleGrantedAuthority(rolemap));
|
null,
|
||||||
}
|
rolesMap.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())
|
||||||
return Mono.just(new UsernamePasswordAuthenticationToken(username, null, authorities));
|
);
|
||||||
} catch (Exception e) {
|
});
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ import org.springframework.web.reactive.config.CorsRegistry;
|
|||||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebFlux
|
@EnableWebFlux
|
||||||
public class CORSFilter implements WebFluxConfigurer {
|
public class CORSFilter implements WebFluxConfigurer {
|
||||||
|
|||||||
@@ -15,37 +15,39 @@ import io.jsonwebtoken.security.Keys;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@Component
|
@Component
|
||||||
public class JWTUtil {
|
public class JWTUtil {
|
||||||
|
|
||||||
@Value("${springbootwebfluxjjwt.jjwt.secret}")
|
@Value("${springbootwebfluxjjwt.jjwt.secret}")
|
||||||
private String secret;
|
private String secret;
|
||||||
|
|
||||||
@Value("${springbootwebfluxjjwt.jjwt.expiration}")
|
@Value("${springbootwebfluxjjwt.jjwt.expiration}")
|
||||||
private String expirationTime;
|
private String expirationTime;
|
||||||
|
|
||||||
private Key key;
|
private Key key;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init(){
|
public void init() {
|
||||||
this.key = Keys.hmacShaKeyFor(secret.getBytes());
|
this.key = Keys.hmacShaKeyFor(secret.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Claims getAllClaimsFromToken(String token) {
|
public Claims getAllClaimsFromToken(String token) {
|
||||||
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
|
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsernameFromToken(String token) {
|
public String getUsernameFromToken(String token) {
|
||||||
return getAllClaimsFromToken(token).getSubject();
|
return getAllClaimsFromToken(token).getSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getExpirationDateFromToken(String token) {
|
public Date getExpirationDateFromToken(String token) {
|
||||||
return getAllClaimsFromToken(token).getExpiration();
|
return getAllClaimsFromToken(token).getExpiration();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean isTokenExpired(String token) {
|
private Boolean isTokenExpired(String token) {
|
||||||
final Date expiration = getExpirationDateFromToken(token);
|
final Date expiration = getExpirationDateFromToken(token);
|
||||||
return expiration.before(new Date());
|
return expiration.before(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateToken(User user) {
|
public String generateToken(User user) {
|
||||||
Map<String, Object> claims = new HashMap<>();
|
Map<String, Object> claims = new HashMap<>();
|
||||||
claims.put("role", user.getRoles());
|
claims.put("role", user.getRoles());
|
||||||
@@ -54,7 +56,7 @@ public class JWTUtil {
|
|||||||
|
|
||||||
private String doGenerateToken(Map<String, Object> claims, String username) {
|
private String doGenerateToken(Map<String, Object> claims, String username) {
|
||||||
Long expirationTimeLong = Long.parseLong(expirationTime); //in second
|
Long expirationTimeLong = Long.parseLong(expirationTime); //in second
|
||||||
final Date createdDate = new Date();
|
final Date createdDate = new Date();
|
||||||
final Date expirationDate = new Date(createdDate.getTime() + expirationTimeLong * 1000);
|
final Date expirationDate = new Date(createdDate.getTime() + expirationTimeLong * 1000);
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
@@ -65,6 +67,7 @@ public class JWTUtil {
|
|||||||
.signWith(key)
|
.signWith(key)
|
||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean validateToken(String token) {
|
public Boolean validateToken(String token) {
|
||||||
return !isTokenExpired(token);
|
return !isTokenExpired(token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,9 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@Component
|
@Component
|
||||||
public class PBKDF2Encoder implements PasswordEncoder{
|
public class PBKDF2Encoder implements PasswordEncoder {
|
||||||
|
|
||||||
@Value("${springbootwebfluxjjwt.password.encoder.secret}")
|
@Value("${springbootwebfluxjjwt.password.encoder.secret}")
|
||||||
private String secret;
|
private String secret;
|
||||||
|
|
||||||
@@ -23,6 +20,7 @@ public class PBKDF2Encoder implements PasswordEncoder{
|
|||||||
|
|
||||||
@Value("${springbootwebfluxjjwt.password.encoder.keylength}")
|
@Value("${springbootwebfluxjjwt.password.encoder.keylength}")
|
||||||
private Integer keylength;
|
private Integer keylength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* More info (https://www.owasp.org/index.php/Hashing_Java) 404 :(
|
* More info (https://www.owasp.org/index.php/Hashing_Java) 404 :(
|
||||||
* @param cs password
|
* @param cs password
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.ard333.springbootwebfluxjjwt.security;
|
package com.ard333.springbootwebfluxjjwt.security;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@@ -12,15 +10,15 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@Component
|
@Component
|
||||||
public class SecurityContextRepository implements ServerSecurityContextRepository{
|
public class SecurityContextRepository implements ServerSecurityContextRepository {
|
||||||
@Autowired
|
|
||||||
private AuthenticationManager authenticationManager;
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
public SecurityContextRepository(AuthenticationManager authenticationManager) {
|
||||||
|
this.authenticationManager = authenticationManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
|
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
@@ -28,17 +26,12 @@ public class SecurityContextRepository implements ServerSecurityContextRepositor
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<SecurityContext> load(ServerWebExchange swe) {
|
public Mono<SecurityContext> load(ServerWebExchange swe) {
|
||||||
ServerHttpRequest request = swe.getRequest();
|
return Mono.justOrEmpty(swe.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION))
|
||||||
String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
.filter(authHeader -> authHeader.startsWith("Bearer "))
|
||||||
|
.flatMap(authHeader -> {
|
||||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
String authToken = authHeader.substring(7);
|
||||||
String authToken = authHeader.substring(7);
|
Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
|
||||||
Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
|
return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
|
||||||
return this.authenticationManager.authenticate(auth).map((authentication) -> {
|
|
||||||
return new SecurityContextImpl(authentication);
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.ard333.springbootwebfluxjjwt.security;
|
package com.ard333.springbootwebfluxjjwt.security;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -9,34 +8,26 @@ import org.springframework.security.config.annotation.web.reactive.EnableWebFlux
|
|||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
/**
|
@AllArgsConstructor
|
||||||
*
|
|
||||||
* @author ard333
|
|
||||||
*/
|
|
||||||
@EnableWebFluxSecurity
|
@EnableWebFluxSecurity
|
||||||
@EnableReactiveMethodSecurity
|
@EnableReactiveMethodSecurity
|
||||||
public class WebSecurityConfig {
|
public class WebSecurityConfig {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AuthenticationManager authenticationManager;
|
private AuthenticationManager authenticationManager;
|
||||||
@Autowired
|
|
||||||
private SecurityContextRepository securityContextRepository;
|
private SecurityContextRepository securityContextRepository;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
|
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
|
||||||
return http
|
return http
|
||||||
.exceptionHandling()
|
.exceptionHandling()
|
||||||
.authenticationEntryPoint((swe, e) -> {
|
.authenticationEntryPoint((swe, e) ->
|
||||||
return Mono.fromRunnable(() -> {
|
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED))
|
||||||
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
).accessDeniedHandler((swe, e) ->
|
||||||
});
|
Mono.fromRunnable(() -> swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN))
|
||||||
}).accessDeniedHandler((swe, e) -> {
|
).and()
|
||||||
return Mono.fromRunnable(() -> {
|
|
||||||
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
|
|
||||||
});
|
|
||||||
}).and()
|
|
||||||
.csrf().disable()
|
.csrf().disable()
|
||||||
.formLogin().disable()
|
.formLogin().disable()
|
||||||
.httpBasic().disable()
|
.httpBasic().disable()
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package com.ard333.springbootwebfluxjjwt.service;
|
package com.ard333.springbootwebfluxjjwt.service;
|
||||||
|
|
||||||
import com.ard333.springbootwebfluxjjwt.model.User;
|
import com.ard333.springbootwebfluxjjwt.model.User;
|
||||||
import com.ard333.springbootwebfluxjjwt.security.model.Role;
|
import com.ard333.springbootwebfluxjjwt.model.security.Role;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -12,28 +13,26 @@ import org.springframework.stereotype.Service;
|
|||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This is just an example, you can load the user from the database from the repository.
|
||||||
*
|
*
|
||||||
* @author ard333
|
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class UserService {
|
public class UserService {
|
||||||
// this is just an example, you can load the user from the database from the repository
|
|
||||||
|
|
||||||
private Map<String, User> data;
|
private Map<String, User> data;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init(){
|
public void init() {
|
||||||
data = new HashMap<>();
|
data = new HashMap<>();
|
||||||
//username:passwowrd -> user:user
|
|
||||||
|
//username:passwowrd -> user:user
|
||||||
data.put("user", new User("user", "cBrlgyL2GI2GINuLUUwgojITuIufFycpLG4490dhGtY=", true, Arrays.asList(Role.ROLE_USER)));
|
data.put("user", new User("user", "cBrlgyL2GI2GINuLUUwgojITuIufFycpLG4490dhGtY=", true, Arrays.asList(Role.ROLE_USER)));
|
||||||
|
|
||||||
//username:passwowrd -> admin:admin
|
//username:passwowrd -> admin:admin
|
||||||
data.put("admin", new User("admin", "dQNjUIMorJb8Ubj2+wVGYp6eAeYkdekqAcnYp+aRq5w=", true, Arrays.asList(Role.ROLE_ADMIN)));
|
data.put("admin", new User("admin", "dQNjUIMorJb8Ubj2+wVGYp6eAeYkdekqAcnYp+aRq5w=", true, Arrays.asList(Role.ROLE_ADMIN)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<User> findByUsername(String username) {
|
public Mono<User> findByUsername(String username) {
|
||||||
if (data.containsKey(username)) {
|
return Mono.justOrEmpty(data.get(username));
|
||||||
return Mono.just(data.get(username));
|
|
||||||
} else {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user