Multi-Tenancy whit JWT autentication and validation of user for tenant - java

I have a project running with JWT authentication, it works, but now I need to implement Multi-Tenancy using the following approach:
Requirements:
A user can have access to one or more tenants
Access permissions are defined by user and tenant
Getting subdomain through #RequestAttribute in requests
Generate the token containing the tenant ID (subdomain).
Validate the tenant on all requests
Implemented:
Created JWT Autentication.
Created TenantInterceptor.
Getting subdomain using #RequestAttribute on requests.
Created existsByUsernameAndSubdomain validation.
I'm having trouble implementing this new feature, can you point me to an implementation example or tutorial that can help me?
I thank you for your help!
Below are my classes or if you prefer clone on GitHub!
My classes
Models:
/** ERole **/
public enum ERole {
ROLE_USER,
ROLE_MODERATOR,
ROLE_ADMIN
}
/** Role **/
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Enumerated(EnumType.STRING)
#Column(length = 20)
private ERole name;
}
/** Tenant **/
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "tenants",
uniqueConstraints = {
#UniqueConstraint(columnNames = "subdomain", name = "un_subdomain")
})
public class Tenant {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 20)
private String subdomain;
#NotBlank
private String name;
}
/** User **/
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username", name = "un_username")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 20)
private String username;
#NotBlank
#Size(max = 120)
#JsonIgnore
private String password;
// Remove
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "users_roles",
joinColumns = {#JoinColumn(name = "user_id",
foreignKey = #ForeignKey(name = "fk_users_roles_users1"))},
inverseJoinColumns = {#JoinColumn(name = "role_id",
foreignKey = #ForeignKey(name = "fk_users_roles_roles1"))})
private Set<Role> roles = new HashSet<>();
// Include
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY)
#JsonManagedReference
private List<UserTenant> tenants = new ArrayList<>();
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
/** UserTenant **/
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users_tenants",
uniqueConstraints = {
#UniqueConstraint(columnNames = "user_id", name = "un_user_id"),
#UniqueConstraint(columnNames = "tenant_id", name = "un_tenant_id")
})
public class UserTenant {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id",
nullable = false,
foreignKey = #ForeignKey(
name = "fk_users_tenants_user1"))
#JsonBackReference
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "tenant_id",
nullable = false,
foreignKey = #ForeignKey(
name = "fk_users_tenants_tenant1"))
#JsonBackReference
private Tenant tenant;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "users_tenants_roles",
joinColumns = {#JoinColumn(name = "user_tenant_id",
foreignKey = #ForeignKey(name = "fk_users_tenants_user_tenant1"))},
inverseJoinColumns = {#JoinColumn(name = "role_id",
foreignKey = #ForeignKey(name = "fk_users_tenants_roles1"))})
private Set<Role> roles = new HashSet<>();
}
Payloads:
/** LoginRequest **/
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class LoginRequest {
#NotBlank
private String username;
#NotBlank
private String password;
}
/** SignupRequest **/
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class SignupRequest {
#NotBlank
#Size(max = 20)
private String username;
#NotBlank
#Size(max = 40)
private String password;
private Set<String> role;
}
/** JwtResponse **/
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class JwtResponse {
private Long id;
private String username;
private List<String> roles;
private String tokenType = "Bearer";
private String accessToken;
public JwtResponse(String accessToken, Long id, String username,
List<String> roles) {
this.id = id;
this.username = username;
this.roles = roles;
this.accessToken = accessToken;
}
}
/** MessageResponse **/
#Data
#Builder
#NoArgsConstructor
public class MessageResponse {
private String message;
public MessageResponse(String message) {
this.message = message;
}
}
Repositories:
/** RoleRepository **/
#Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(ERole name);
}
/** UserRepository **/
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
}
/** UserTenantRepository **/
#Repository
public interface UserTenantRepository extends JpaRepository<UserTenant, Long> {
#Query("SELECT ut FROM UserTenant ut WHERE ut.user.username = :username AND ut.tenant.subdomain = :subdomain ")
Optional<UserTenant> findByUserAndSubdomain(String username, String subdomain);
#Query("SELECT " +
"CASE WHEN COUNT(ut) > 0 THEN true ELSE false END " +
"FROM UserTenant ut " +
"WHERE ut.user.username = :username " +
"AND ut.tenant.subdomain = :subdomain ")
Boolean existsByUsernameAndSubdomain(String subdomain, String username);
}
Services:
/** AuthService **/
#Service
#RequiredArgsConstructor
public class AuthService {
private final UserRepository userRepository;
private final AuthenticationManager authenticationManager;
private final JwtUtils jwtUtils;
private final PasswordEncoder encoder;
private final RoleRepository roleRepository;
public JwtResponse authenticateUser(String subdomain, LoginRequest loginRequest) {
System.out.println(subdomain);
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
System.out.println(authentication);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
return new JwtResponse(jwt,
userDetails.getId(),
userDetails.getUsername(),
roles);
}
#Transactional
public MessageResponse registerUser(SignupRequest signUpRequest) {
// Create new user's account
User user = new User(
signUpRequest.getUsername(),
encoder.encode(signUpRequest.getPassword()));
Set<String> strRoles = signUpRequest.getRole();
Set<Role> roles = new HashSet<>();
if (strRoles == null) {
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
} else {
strRoles.forEach(role -> {
switch (role) {
case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(adminRole);
break;
case "mod":
Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(modRole);
break;
default:
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
}
});
}
user.setRoles(roles);
userRepository.save(user);
return new MessageResponse("User registered successfully!");
}
}
/** UserDetailsImpl **/
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 1L;
private final Long id;
private final String username;
#JsonIgnore
private final String password;
private final Collection<? extends GrantedAuthority> authorities;
public UserDetailsImpl(Long id, String username, String password,
Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.password = password;
this.authorities = authorities;
}
public static UserDetailsImpl build(User user) {
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name()))
.collect(Collectors.toList());
return new UserDetailsImpl(
user.getId(),
user.getUsername(),
user.getPassword(),
authorities);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public Long getId() {
return id;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
UserDetailsImpl user = (UserDetailsImpl) o;
return Objects.equals(id, user.id);
}
}
/** UserDetailsServiceImpl **/
#Service
#RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
private final UserTenantRepository userTenantRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
return UserDetailsImpl.build(user);
}
}
Controller
/** AuthController **/
#RestController
#RequestMapping("/auth")
#RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
private final UserRepository userRepository;
private final UserTenantRepository userTenantRepository;
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(
#RequestAttribute String subdomain,
#Valid #RequestBody LoginRequest loginRequest
) {
if (!userTenantRepository.existsByUsernameAndSubdomain(subdomain, loginRequest.getUsername())) {
return ResponseEntity
.badRequest()
.body(new MessageResponse("Unauthorized: This username and tenant is not authorized!"));
}
return ResponseEntity.ok(authService.authenticateUser(subdomain, loginRequest));
}
#PostMapping("/signup")
public ResponseEntity<?> registerUser(#Valid #RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest.getUsername())) {
return ResponseEntity
.badRequest()
.body(new MessageResponse("Error: Username is already taken!"));
}
return ResponseEntity.ok(authService.registerUser(signUpRequest));
}
}
JWT:
/** AuthEntryPointJwt **/
#Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: incorrect username or password");
}
}
/** AuthTokenFilter **/
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired
private JwtUtils jwtUtils;
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response
, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
String serverName = request.getServerName();
String subdomain = serverName.substring(0, serverName.indexOf("."));
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
System.out.println(userDetails);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
e.printStackTrace();
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
return null;
}
}
/** JwtUtils **/
#Component
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
#Value("${example.app.jwtSecret}")
private String jwtSecret;
#Value("${example.app.jwtExpirationMs}")
private int jwtExpirationMs;
public String generateJwtToken(Authentication authentication) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
return Jwts.builder()
.setSubject((userPrincipal.getUsername()))
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUserNameFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) {
logger.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) {
logger.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
logger.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) {
logger.error("JWT claims string is empty: {}", e.getMessage());
}
return false;
}
}
Utils:
/** TenantInterceptor **/
public class TenantInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String serverName = request.getServerName();
String tenantId = serverName.substring(0, serverName.indexOf("."));
request.setAttribute("subdomain", tenantId);
return true;
}
}
/** WebSecurityConfig **/
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig implements WebMvcConfigurer {
final
UserDetailsServiceImpl userDetailsService;
private final AuthEntryPointJwt unauthorizedHandler;
public WebSecurityConfig(UserDetailsServiceImpl userDetailsService, AuthEntryPointJwt unauthorizedHandler) {
this.userDetailsService = userDetailsService;
this.unauthorizedHandler = unauthorizedHandler;
}
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers(
"/auth/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/configuration/**",
"/swagger-resources/**",
"/webjars/**",
"/api-docs/**").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().authenticated();
http.authenticationProvider(authenticationProvider());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TenantInterceptor());
}
}

I was able to solve the problem by modifying the loadUserByUsername method in the UserDetailsServiceImpl.
See the implementation details on GitHub!
#Service
#RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserTenantRepository userTenantRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// Getting subdomain from request attributes
HttpServletRequest request =
((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
.getRequest();
String serverName = request.getServerName();
String subdomain = serverName.substring(0, serverName.indexOf("."));
UserTenant userTenant = userTenantRepository.findByUserAndSubdomain(username, subdomain)
.orElseThrow(() -> new UsernameNotFoundException(
"UserTenant Not Found with username: " + username + " and " + subdomain));
// Getting Rules from the UserTenant
List<GrantedAuthority> authorities = userTenant.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name()))
.collect(Collectors.toList());
return new UserDetailsImpl(
userTenant.getUser().getId(),
userTenant.getUser().getUsername(),
userTenant.getUser().getPassword(),
authorities
);
}
}
Inserts in Database
INSERT INTO roles(id, name)
VALUES (1 ,'ROLE_USER'),
(2, 'ROLE_MODERATOR'),
(3, 'ROLE_ADMIN');
INSERT INTO tenants (id, name, subdomain)
VALUES (1, 'Tenant 1', 'tenant1'),
(2, 'Tenant 2', 'tenant2');
# user, password
# user1, user1
# user2, user2
INSERT INTO users (id, username, password)
VALUES (1, 'user1', '$2a$10$wFMJLxdXKGRa8lJO6k2DAOnW9HstAPoHecXUNkDyYNeaNnZJAz.hy'),
(2, 'user2', '$2a$10$Z9/wLkmf5IwfjJqIQU6X.OBFg3TCBUyk3bdfgkGjU0.HI5kVibZxG');
INSERT INTO users_tenants (id, tenant_id, user_id)
VALUES (1, 1, 1),
(2, 2, 2);
INSERT INTO users_tenants_roles (user_tenant_id, role_id)
VALUES (1, 2),
(1, 3),
(2, 1);
INSERT INTO items (id, tenant_id, name)
VALUES (1, 1, 'Product 1 in Tenant 1'),
(2, 1, 'Product 2 in Tenant 1'),
(3, 2, 'Product 1 in Tenant 2'),
(4, 2, 'Product 2 in Tenant 2');
Validations in Postman
Created token variable in Postman:
Set token value in Postman variable:
Added Authorization variable in requests headers:
Validating if the domain exists in sign in:
Validating user access permission on tenant:
Authorized login:
Getting items of tenant1:
Trying to get tenant2 list of items without being logged in tenant2 and having access permissions:
Logging in with user2 in tenant2:
Getting items list tenant2
Trying to get tenant1 list of items without being logged in tenant1 and having access permissions:

Related

Spring Security configuration with role-based access returns a 403 error

I am developing a small application with Spring Security configuration and JWT token-based authorization and encountered a problem - when using hasRole() or hasPermission() methods to differentiate access to requests, even if the authorized user has the roles that meet the restrictions, the request still returns a 403 code.
Implementing of UserDetails and UserDetailsService
#RequiredArgsConstructor
public class UserDetailsImpl implements UserDetails {
private final User user;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles();
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return user.getIsEnable();
}
#Override
public boolean isAccountNonLocked() {
return user.getIsEnable();
}
#Override
public boolean isCredentialsNonExpired() {
return user.getIsEnable();
}
#Override
public boolean isEnabled() {
return user.getIsEnable();
}
}
#Service
#RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String exceptionMsg = String.format("User with username '%s' not found", username);
return new UserDetailsImpl(userRepository.findUserByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(exceptionMsg)));
}
}
User entity and Roles
#Entity(name = "users")
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(of = {"id", "username"})
public class User {
#Transient
private final String MAIL_REGEX =
"^\\w+([\\.-]?\\w+)*#\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$";
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(unique = true, nullable = false)
#NotBlank(message = "Username cannot be empty")
#NotNull(message = "Username cannot be empty")
#Size(min = 1, max = 90)
private String username;
#Column(nullable = false)
#NotBlank(message = "Password cannot be empty")
#NotNull(message = "Password cannot be empty")
#Size(min = 8, max = 100)
private String password;
#Column(unique = true, nullable = false)
#Email(message = "Invalid email address", regexp = MAIL_REGEX)
#NotBlank(message = "Email cannot be empty")
private String email;
#ElementCollection(fetch = EAGER)
private Set<Role> roles = Set.of(Role.USER, Role.ADMIN, Role.SUPER_ADMIN);
#Column(nullable = false)
private Boolean isEnable = true;
}
public enum Role implements GrantedAuthority {
USER, ADMIN, SUPER_ADMIN;
#Override
public String getAuthority() {
return name();
}
}
Security configuration
#Configuration
public class SecurityConfig {
#Value("${jwt.public.key}")
RSAPublicKey publicKey;
#Value("${jwt.private.key}")
RSAPrivateKey privateKey;
#Bean
PasswordEncoder encoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeHttpRequests().requestMatchers("/login", "/register").permitAll();
http.authorizeHttpRequests().requestMatchers("/hello").hasAuthority("USER");
http.httpBasic(Customizer.withDefaults());
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
http.sessionManagement(session ->
session.sessionCreationPolicy(STATELESS));
http.exceptionHandling()
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler());
return http.build();
}
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(publicKey).build();
}
#Bean
JwtEncoder jwtEncoder() {
JWK jwk = new RSAKey.Builder(publicKey).privateKey(privateKey).build();
JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwkSource);
}
}
If I try to query the /hello path, it returns a forbidden error, despite the fact that the authorized user has all the possible roles. Can you tell me what could be the problem? A long time ago I did everything exactly as it is now and everything worked without errors.
Tried to add ROLE_ prefix, make roles without enum class with clear string format, applied both hasRole() and hasAuthority() methods

How to connect mongodb using Springboot through mongoose?

I have already done connect MongoDB using application.properties. But I want to connect MongoDB through mongoose.
This is my current configuration
This is DB Connection url setting in application.properties;
spring.data.mongodb.uri =mongodb+srv://hans123:Hans123#cluster0.avxi858.mongodb.net/?retryWrites=true&w=majority
spring.data.mongodb.database=test
spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost
Model Class
#Document
#AllArgsConstructor
#NoArgsConstructor
#Data
public class User {
#Id
#Indexed
private String id;
#Indexed
private String address;
#Indexed
private String name;
#Indexed
private String email;
#Indexed
private String password;
#Indexed
private String role;
}
Repository Class
public interface userReporsitory extends MongoRepository<User,String> {
Optional<User> findByEmail(String email);
List<User> findAllByRole(String role);
}
Service Class
#AllArgsConstructor
#Service
public class userService {
private userReporsitory userReporsitory;
public User saveUser(User user){
return userReporsitory.save(user);
}
public User login(User user){
User response = userReporsitory.findByEmail(user.getEmail()).orElseThrow(()->new RuntimeException("User Not Found"));
if(!response.getPassword().equals(user.getPassword())){
throw new RuntimeException("Bad Credincials");
}
return response;
}
public List<User> findAllUsers(){
return userReporsitory.findAllByRole("user");
}
}
Controller Class
#CrossOrigin
#RestController
#AllArgsConstructor
#RequestMapping("api/v1/user")
public class userController {
private userService userService;
#PostMapping("/create")
public ResponseEntity<User> save(#RequestBody User user){
HttpStatus status = HttpStatus.EXPECTATION_FAILED;
User response = userService.saveUser(user);
if(response != null){
status = HttpStatus.CREATED;
}
return new ResponseEntity<>(response, status);
}
#PostMapping("/login")
public ResponseEntity<User> login(#RequestBody User user){
return new ResponseEntity<>(userService.login(user),HttpStatus.ACCEPTED);
}
#GetMapping("/userList")
public ResponseEntity<List<User>> userList(){
return new ResponseEntity<>(userService.findAllUsers(),HttpStatus.ACCEPTED);
}
}
In above answer you can encrypt the password as well (Additional Thing)
AuthRequest Class:
#Data
#AllArgsConstructor
public class AuthRequestDto {
private String userName;
private String password;
}
RegisterRequest Class:
#Data
#AllArgsConstructor
public class RegisterRequestDto {
private String userName;
private String password;
private String firstName;
private String email;
private String phone;
private String address;
}
UserService Class:
#AllArgsConstructor
#Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final JWTUtility jwtUtility;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity userEntity = userRepository.findByUsername(username).orElseThrow(()-> new UsernameNotFoundException("user name not found"));
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(userEntity.getRole());
return new User(userEntity.getUsername() , userEntity.getPassword() , new ArrayList<>(Arrays.asList(authority)));
}
public UserEntity registerUser(RegisterRequestDto request){
//check user is already in
if(userRepository.existsByUsernameIgnoreCase(request.getUserName())){
throw new FieldValidationFaild("user name already exist in the system");
}
if(userRepository.existsByEmailIgnoreCase(request.getEmail())){
throw new FieldValidationFaild("email already exist in the system");
}
UserEntity saveToBe = UserEntity.builder()
.username(request.getUserName())
.password(new BCryptPasswordEncoder().encode(request.getPassword()))
.firstName(request.getFirstName())
.email(request.getEmail())
.phone((request.getPhone()))
.address(request.getAddress())
.role(Role.BUYER)
.build();
return userRepository.save(saveToBe);
}
public AuthRespondDto loginUser(AuthRequestDto authRequestDto){
final UserDetails userDetails = loadUserByUsername(authRequestDto.getUserName());
final String token = jwtUtility.generateToken(userDetails);
return new AuthRespondDto(token , authRequestDto.getUserName() , userDetails.getAuthorities().stream().findFirst().get().getAuthority());
}
public long getAuthUserId(String username){
UserEntity userEntity = userRepository.findByUsername(username).get();
return userEntity.getId();
}
public String getUserAddressByName(String username){
UserEntity userEntity = userRepository.findByUsername(username).get();
return userEntity.getAddress();
}
public String getUserPhoneByName(String username){
UserEntity userEntity = userRepository.findByUsername(username).get();
return userEntity.getPhone();
}
public String getUserEmailByName(String username){
UserEntity userEntity = userRepository.findByUsername(username).get();
return userEntity.getEmail();
}
public UserEntity getUserById(long id){
return userRepository.findById(id).orElseThrow(()-> new NotFoundException("user not found"));
}
}
AuthController Class (Controller Class):
public class AuthController {
private final AuthenticationManager authenticationManager;
private final UserService userService;
#PostMapping("/signin")
public ResponseEntity<AuthRespondDto> login(#RequestBody AuthRequestDto authRequestDto) throws BadCredentialsException {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authRequestDto.getUserName(),
authRequestDto.getPassword()
)
);
}catch (BadCredentialsException e){
//TODO add proper exception
throw new BadCredentialsException("INVALID_USERNAME_OR_PASSWORD" , e);
}
return new ResponseEntity<>(userService.loginUser(authRequestDto) , HttpStatus.ACCEPTED);
}
#PostMapping("/signup")
public ResponseEntity<UserEntity> register(#RequestBody RegisterRequestDto request){
// basic controller level validations
if(request.getUserName().isEmpty()){
throw new FieldValidationFaild("user name is required");
}
if(request.getPassword().isEmpty()){
throw new FieldValidationFaild("password is required");
}
if(request.getFirstName().isEmpty()){
throw new FieldValidationFaild("first name is required");
}
if(request.getEmail().isEmpty()){
throw new FieldValidationFaild("email is required");
}
if(request.getPhone().isEmpty()){
throw new FieldValidationFaild("phone is required");
}
if(request.getPhone().length() != 10){
throw new FieldValidationFaild("phone number length must be 10");
}
if(request.getAddress().isEmpty()){
throw new FieldValidationFaild("address is required");
}
return new ResponseEntity<>(userService.registerUser(request) , HttpStatus.CREATED);
}
}

403 forbidden instead of successful authorization! Why?

I am writing a Spring-MVC application using Spring Boot and Hibernate. The application has a User who has the role of Admin and Customer. I decided to add Spring Security JWT and did everything right. Now that I have everything ready, I want to get a TOKEN, I log in successfully, but in return I get 403 Forbidden. What have I done wrong? Did you configure configs incorrectly or did the roles incorrectly?
Configs:
#RequiredArgsConstructor
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// Fields
//
private final JwtTokenProvider jwtTokenProvider;
//
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().disable().csrf().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
}
Controller:
#RequiredArgsConstructor
#RestController
#RequestMapping("/auth")
public class AuthenticationController {
// Fields
//
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
private final UserService userService;
//
// GET-Methods
//
//
#PostMapping("/login")
public ResponseEntity<Map<String, String>> login(#RequestBody AuthenticationRequestDTO requestDto) {
try {
String login = requestDto.getLogin();
authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(login, requestDto.getPassword()));
User user = userService.findByLogin(login);
String token = jwtTokenProvider.createToken(login, user.getRole());
Map<String, String> response = new HashMap<>();
response.put("login", login);
response.put("token", token);
return ResponseEntity.ok(response);
} catch (AuthenticationException e) {
throw new BadCredentialsException("Invalid login or password");
}
}
}
User class:
#Getter
#Entity
#NoArgsConstructor
#Table(name = "users")
public class User {
// Fields
//
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "last_name")
private String lastName;
private String login;
private String password;
private String mail;
private boolean isDeleted;
#Enumerated(EnumType.STRING)
private Role role;
}
#RequiredArgsConstructor
#Component
public class JwtTokenProvider {
// Fields
//
private final UserDetailsService userDetailsService;
#Value("${jwt.token.secret}")
private String secret;
#Value("${jwt.token.expired}")
private Long validityInMilliSeconds;
//
// METHODS
//
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(8);
}
#PostConstruct
protected void init() {
secret = Base64.getEncoder().encodeToString(secret.getBytes());
}
/**
* Генерируем ТОКЕН
*
* #param login
* #param role
* #return ТОКЕН
*/
public String createToken(String login, Role role) {
Claims claims = Jwts.claims().setSubject(login);
claims.put("roles", getRoleName(role));
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliSeconds);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(getLogin(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getLogin(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
throw new JwtAuthenticationException("JWT token is expired or invalid");
}
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer_")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
private String getRoleName(Role role) {
String roleName = role.name();
return roleName;
}
}
#RequiredArgsConstructor
public class JwtTokenFilter extends GenericFilterBean {
private final JwtTokenProvider jwtTokenProvider;
#Override
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}
#RequiredArgsConstructor
#Service
#Slf4j
public class JwtUserDetailsService implements UserDetailsService {
private final UserService userService;
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
User user = userService.findByLogin(login);
JwtUser jwtUser = JwtUserFactory.create(user);
log.info("IN loadUserByUsername - user with login: {} successfully loaded", login);
return jwtUser;
}
}
#AllArgsConstructor
#Getter
public class JwtUser implements UserDetails {
// Fields
//
private Long id;
private String name;
private String lastName;
private String login;
private String password;
private String mail;
private boolean isDeleted;
private Role role;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role));
}
#Override
public String getUsername() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
}
P.S. I will not throw all the code for JWT from the project here, but skip only the most important thing.
LOGS:
2020-08-10 16:36:27.343 INFO 7556 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 64 ms
2020-08-10 16:36:28.454 INFO 7556 --- [nio-8080-exec-2] r.c.security.JwtUserDetailsService : IN loadUserByUsername - user with login: art123 successfully loaded

Create JWT security for two entities

I've got two entities as two type of account in my spring app:
Customer:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "customer_id",updatable = false)
private String customerId;
#NotBlank(message = "Nickname may not be blank")
#Size(min = 3, max = 20, message = "Nickname '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "nickname",updatable = false)
private String nickname;
#NotBlank(message = "City may not be blank")
#Size(min = 3, max = 25, message = "City '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "city")
private String city;
#NotBlank(message = "Phone Number may not be blank")
#Pattern(regexp="(^$|[0-9]{9})")
#Column(name = "phone_number",updatable = false)
private String phoneNumber;
#NotBlank(message = "Email may not be blank")
#Email
#Column(name = "mail",updatable = false)
private String mail;
#NotBlank(message = "Password may not be blank")
#Size(min = 5 , max = 30, message = "Password '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "password")
private String password;
// getters setters
}
and Specialist:
#Entity
#Table(name = "specialist")
public class Specialist {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "specialist_id",updatable = false)
private String specialistId;
#NotBlank(message = "Name may not be blank")
#Size(min = 3, max = 20, message = "Name '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "name",updatable = false)
private String name;
#NotBlank(message = "Surname may not be blank")
#Size(min = 3, max = 20, message = "Name '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "surname",updatable = false)
private String surname;
#NotNull(message = "Province may not be blank")
#Column(name = "province")
private Province province;
#NotBlank(message = "City may not be blank")
#Size(min = 3, max = 25, message = "City '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "city")
private String city;
#NotBlank(message = "Profession may not be blank")
#Size(min = 3, max = 25, message = "Profession '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "profession")
private String profession;
#NotBlank(message = "Phone Number may not be blank")
#Pattern(regexp="(^$|[0-9]{9})")
#Column(name = "phone_number",updatable = false)
private String phoneNumber;
#NotBlank(message = "Email may not be blank")
#Email
#Column(name = "mail",updatable = false)
private String mail;
#NotBlank(message = "Password may not be blank")
#Size(min = 5 , max = 30, message = "Password '${validatedValue}' isn't correct => must be between {min} and {max} characters")
#Column(name = "password")
private String password;
#Column(name = "rate")
private HashMap<String,Double> rateStars;
#Range(min = 0 , max = 5)
#Column(name = "average_rate")
private Double averageRate;
//getters setters
}
As you can see this entities have got differently required fields.
Now I want to create JWT where specialist and customer have got other access to endpoints.
For example specialist has got other header than customer in front end.
And now i ask question to You, how connect two differently entities to JWT security? Because all of tutorials/posts in internet concerns JWT only to one User entity and then separate role.
I tried to solve this but I stand with this how to supersede User entity and change this to my Specialist and Customer entities:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if(user == null) new UsernameNotFoundException("User not found");
return user;
}
#Transactional
public User loadUserById(Long id){
User user = userRepository.getById(id);
if(user == null) new UsernameNotFoundException("User not found");
return user;
}
#Component
public class UserValidator implements Validator {
#Override
public boolean supports(Class<?> aClass) {
return User.class.equals(aClass);
}
#Override
public void validate(Object object, Errors errors) {
User user = (User) object;
if(user.getPassword().length() < 6){
errors.rejectValue("password","Length","Password must be at least 6 characters");
}
if(!user.getPassword().equals(user.getConfirmPassword())){
errors.rejectValue("confirmPassword","Match","Passwords must match");
}
}
}
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
InvalidLoginResponse loginResponse = new InvalidLoginResponse();
String jsonLoginResponse = new Gson().toJson(loginResponse);
httpServletResponse.setContentType("application/json");
httpServletResponse.setStatus(401);
httpServletResponse.getWriter().print(jsonLoginResponse);
}
}
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
try{
String jwt = getJWTFromRequest(httpServletRequest);
if(StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)){
Long userId = tokenProvider.getUserIdFromJWT(jwt);
User userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails,null, Collections.emptyList()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}catch (Exception ex){
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
private String getJWTFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader(HEADER_STRING);
if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)){
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
#Component
public class JwtTokenProvider {
public String generateToken(Authentication authentication){
User user = (User)authentication.getPrincipal();
Date now = new Date(System.currentTimeMillis());
Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
String userId = Long.toString(user.getId());
Map<String,Object> claims = new HashMap<>();
claims.put("id",(Long.toString(user.getId())));
claims.put("username", user.getUsername());
claims.put("fullName", user.getFullName());
return Jwts.builder()
.setSubject(userId)
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public boolean validateToken(String token) {
try{
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
return true;
}catch (SignatureException ex){
System.out.println("Invalid JWT Signature");
}catch (MalformedJwtException ex){
System.out.println("Invalid JWT Token");
}catch (ExpiredJwtException ex){
System.out.println("Expired JWT token");
}catch (UnsupportedJwtException ex){
System.out.println("Unsupported JWT token");
}catch (IllegalArgumentException ex){
System.out.println("JWT claims string is empty");
}
return false;
}
public Long getUserIdFromJWT(String token){
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
String id = (String)claims.get("id");
return Long.parseLong(id);
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unAuthorizedHandler;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(){
return new JwtAuthenticationFilter();
}
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unAuthorizedHandler).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.headers().frameOptions().sameOrigin() //To enable H2 db
.and()
.authorizeRequests()
.antMatchers(
"/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers(SIGN_UP_URLS).permitAll()
.antMatchers(H2_URL).permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
all of tutorials/posts in internet concerns JWT only to one User entity and then separate role.
You may store users in two tables but they are users of two different roles- Customer and Specialist
how connect two differently entities to JWT security?
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/**
* logic to check if this username belongs to Customer/Specialist
* based on that create a new Authority - ROLE_CUSTOMER or ROLE_SPECIALIST
*/
}
specialist has got other header than customer in front end.
These are Menu if I am not wrong. Based on Authority that you passed using above method, you can show Menu.

Spring end-point always return 403 while checking role

I have integrated the role and want to manage the access of the specific service on the basis of the role.
An admin can create an AGENT and that agent came under the group of ADMIN user.
What I basically did have create a 1 to Many relation ship because my user could have only 1 role.
#Entity
#Table(name = "role")
public class Role {
private Long id;
private String name;
private Collection<User> users;
public Role() {
}
public Role(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
public Collection<User> getUsers() {
return users;
}
public void setUsers(Collection<User> users) {
this.users = users;
}
}
And here I have the user which the group relation as well the group is also 1 TO MANY because a user_admin can have multiple agents but agent could not have multiple admins.
#Entity
#Table(name = "user")
public class User {
private long id;
private String username;
private String password;
private boolean enabled = false;
private Role role;
private UserGroup userGroup;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User(String username, String password, Role role) {
this.username = username;
this.password = password;
this.role = role;
}
public User(String username, String password, boolean enabled, Role role, UserGroup userGroup) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.role = role;
this.userGroup = userGroup;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#NotBlank
#Column(nullable = false, updatable = false, unique = true)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#NotBlank
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(nullable = false, updatable = true)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id")
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
#ManyToOne
#JoinColumn(name = "user_group_id", referencedColumnName = "id")
public UserGroup getUserGroup() {
return userGroup;
}
public void setUserGroup(UserGroup userGroup) {
this.userGroup = userGroup;
}
}
At the time of creation of the user I specify the role and the group as well.
And in the SecurityConfig I have configure like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(Constants.BASE_URL_FILE_UPLOADER + Constants.URL_UPLOAD_FILE).hasRole("ADMIN") .anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.exceptionHandling().authenticationEntryPoint(new ContentSearcherAuthenticationEntryPoint());
}
But If I am accessing this end-point with the admin user I throws forbidden and also in the function when I access authentication.getAuthorities()it return emptyList
ResponseEntity<JsonNode> uploadFile(#RequestParam("file") MultipartFile file, Authentication authentication) {
logger.info("Authentication is [{}] and user is [{}]", authentication.getAuthorities(), authentication.getName()); // []
}
I am confused in UserDetailsService I have also added the GrantedAuthority as well like this.
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
private RoleRepository roleRepository;
public UserDetailsServiceImpl(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username + " Not Exists");
}
user.setEnabled(true);
userRepository.save(user);
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.isEnabled(),
true, true, true, getAuthorities(user.getRole()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Role role) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Constants.ROLE_PREFIX + role.getName()));
return authorities;
}
}
What I am missing is there any more configuration I have to add ?
I am using JWT for the authentication purpose some thing should not to be added in this as well ?
Because when I get the Authentication in the JWT successfulAuthentication it shows the Authorities.
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Auth in the successful is [{}]", authResult.getAuthorities()); // [ROLE_ADMIN]
}
Using Spring-Security core 5.0.9.
I did it after getting the hint from #Reza Nasiri question basically I was not adding the Authorities when authenticating the JWT token now what I did is,
return new UsernamePasswordAuthenticationToken(user, null, getAuthorities("ADMIN");
In my getAuthentication function in JWTAuthorizationFilter class.

Categories