0
\$\begingroup\$

I am trying to implement an authentication via Spring Security. In all tutorials people build a List of GrantedAuthority records from the List of UserRole records . I feel uncomfortable to re-create this list on a fly each time when I need it, even if I know that it contains 2-3 records.

My solution is to implement spring GrantedAuthority interface in my Model class. I checked the source code of spring SimpleGrantedAuthority and made my implementation based on it. The only one problem is that SimpleGrantedAuthority takes String role name in a constructor and throws an exception if it is NULL. I can not do that as my model is managed by Hibernate that use no-arguments constructor. So I had to add NOT_AUTHORIZED role which is default role value.

please give me a few pieces of an advice:

  • Is my implementation secured, does it have some issues and underwater rocks ?

  • Is it a good idea in case of projet structure to implement GrantedAuthority in the UserRole Model ?

  • I override "equals" method of UserRole Model, can it be a problem for the Hibernate ?

Here is the implementation

package com.art.backend.models;

import com.art.backend.models.inherited.ModelBase;
import com.art.backend.utils.RestVisibilityMode;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.persistence.*;

@Entity
@Table(name="art_user_role")
public class ModelUserRole extends ModelBase implements GrantedAuthority {

    public enum Role {
        NOT_AUTHORIZED,
        USER,
        ADMIN;
    }

    private Role role = Role.NOT_AUTHORIZED;

    public ModelUserRole(){
        super();
    }

    public ModelUserRole(Role role){
        this();
        this.role = role;
    }

    @Override
    @Transient
    public String getAuthority() {
       return role.name();
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == null) return false;

        return (obj instanceof ModelUserRole) && this.role == ((ModelUserRole) obj).role;
    }

    @Override
    public int hashCode() {
        return this.role.name().hashCode();
    }

    @Override
    public String toString() {
        return this.role.name();
    }


    @JsonView(RestVisibilityMode.ShortInfo.class)
    @Enumerated(EnumType.STRING)
    @Column(name="role_name", nullable = false, unique = true)
    public Role getRole() {
        return role;
    }

    private void setRole(Role role) {
        this.role = role;
    }

}

I also provide ModelBase and ModelUser classed, but they are not really important

BaseModel

package com.art.backend.models.inherited;

import com.art.backend.configs.ConfigGlobal;
import com.art.backend.models.annotations.IgnoreInUpdate;
import com.art.backend.utils.Const;
import com.art.backend.utils.RestVisibilityMode;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.*;
import java.sql.Timestamp;

@MappedSuperclass
public abstract class ModelBase {

    private Integer id;
    private Timestamp createdAt;

    @Transient
    public String getModelName(){
        Table annotationTable = this.getClass().getAnnotation(Table.class);
        return (annotationTable != null) ? annotationTable.name() : Const.STR_NULL;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    @JsonView(RestVisibilityMode.IdOnly.class)
    public Integer getId() {
        return id;
    }

    private void setId(Integer id) {
        this.id = id;
    }

    @CreationTimestamp
    @Column(name="created_at", updatable = false, nullable = false)
    @JsonFormat(pattern = ConfigGlobal.FORMAT_DATE_TIME)
    @JsonView(RestVisibilityMode.FullInfo.class)
    @IgnoreInUpdate
    public Timestamp getCreatedAt() {
        return createdAt;
    }

    private void setCreatedAt(Timestamp createdAt) {
        this.createdAt = createdAt;
    }
}

ModelUser

package com.art.backend.models;

import com.art.backend.models.inherited.ModelBase;
import com.art.backend.utils.RestVisibilityMode;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;


import javax.persistence.*;
import java.util.Set;

@Entity
@Table(name="art_user")
public class ModelUser extends ModelBase implements UserDetails {

    public enum Status{
        NOT_CONFIRMED,
        ACTIVE,
        BANNED,
        DELETED
    }

    private Status status;
    private String username;
    private String password;
    private String email;


    private Set<ModelUserRole> roles;

    @Enumerated(EnumType.STRING)
    @JsonView(RestVisibilityMode.TechnicalInfo.class)
    @Column(name = "status", nullable = false, length = 64)
    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    @JsonView(RestVisibilityMode.ShortInfo.class)
    @Column(name="email")
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
            name = "art_user_art_user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "user_role_id")}
    )
    @ElementCollection(targetClass=ModelUserRole.class)
    public Set<ModelUserRole> getRoles() {
        return roles;
    }

    public void setRoles(Set<ModelUserRole> roles) {
        this.roles = roles;
    }

    @Override
    @Transient
    public Set<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }

    @Override
    @JsonIgnore
    @Column(name="password")
    public String getPassword() {
        return password;
    }

    private void setPassword(String password){
        this.password = password;
    }

    @Override
    @JsonView(RestVisibilityMode.ShortInfo.class)
    @Column(name="username", nullable = false, unique = true)
    public String getUsername() {
        return username;
    }

    private void setUsername(String username){
        this.username = username;
    }

    @Override
    @Transient
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    @Transient
    public boolean isAccountNonLocked() {
        return Status.ACTIVE == status;
    }

    @Override
    @Transient
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    @Transient
    public boolean isEnabled() {
        return Status.ACTIVE == status;
    }

}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.