Bagaimana cara memeriksa "hasRole" di Java Code dengan Spring Security?

118

Bagaimana cara memeriksa otoritas atau izin pengguna di Kode Java? Misalnya - Saya ingin menampilkan atau menyembunyikan tombol untuk pengguna tergantung pada peran. Ada anotasi seperti:

@PreAuthorize("hasRole('ROLE_USER')")

Bagaimana cara membuatnya dalam kode Java? Sesuatu seperti :

if(somethingHere.hasRole("ROLE_MANAGER")) {
   layout.addComponent(new Button("Edit users"));
}
Piotr Gwiazda
sumber

Jawaban:

70

Spring Security 3.0 memiliki API ini

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Anda harus menyuntikkan pembungkusnya, sebelum Anda menggunakannya.

SecurityContextHolderAwareRequestWrapper

JoseK
sumber
53
Saat jawaban Anda dinyatakan, sepertinya metode ini statis, namun Anda memerlukan SecurityContextHolderAwareRequestWrappercontoh. Anda dapat memperbaikinya dengan menjelaskan cara mendapatkannya dan mengklarifikasi jawaban itu sendiri sedikit lebih banyak.
Xtreme Biker
3
Bagaimana cara mendapatkan kembali pembungkusnya di Controller?
Alfonso Tienda
3
Bagaimana saya bisa mendapatkan instance SecurityContextHolderAwareRequestWrapper?
gstackoverflow
2
Xtreme Biker benar, Bagaimana Anda mendapatkan kelas SecurityContextHolderAwareRequestWrapper? Ini bukan objek statis.
Cobalah
5
Jika ini adalah aplikasi web, yang tampaknya bukan, Anda bisa menambahkan SecurityContextHolderAwareRequestWrapper sebagai parameter. Dan jika itu adalah aplikasi web, Anda cukup mendeklarasikan HttpServletRequest sebagai parameter dan memanggil isUserInRole
David Bradley
144

Anda dapat menggunakan metode isUserInRole dari objek HttpServletRequest.

sesuatu seperti:

public String createForm(HttpSession session, HttpServletRequest request,  ModelMap   modelMap) {


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}
gouki
sumber
saya pikir lebih mudah untuk diuji
fego
1
tapi kalau saya belum request?
gstackoverflow
Bagaimana ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()untuk mendapatkan permintaan tersebut? :)
Petr Újezdský
4
@Tokopedia ?
Pascal
Dan itu bahkan bukan API Musim Semi, spesifikasi Servlet biasa! Sayang sekali bukan itu jawaban yang dipilih
gregfqt
67

Alih-alih menggunakan loop untuk menemukan otoritas dari UserDetails, Anda dapat melakukan:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
Ninca
sumber
2
Jawaban yang jauh lebih bagus, namun ROLE_ADMIN harus dalam tanda kutip ganda.
Erica Kane
6
Ini sangat beresiko. Ketahuilah bahwa beralih ke implementasi lain dari implementasi GrantedAuthority (mis. JAAS, dengan menambahkan kemungkinan otorisasi lain) akan membuat kode ini tidak berfungsi. Lihat implementasi equals () di SimpleGrantedAuthority
Petr Újezdský
47

Anda dapat mengambil konteks keamanan dan kemudian menggunakannya:

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;

    protected boolean hasRole(String role) {
        // get security context from thread local
        SecurityContext context = SecurityContextHolder.getContext();
        if (context == null)
            return false;

        Authentication authentication = context.getAuthentication();
        if (authentication == null)
            return false;

        for (GrantedAuthority auth : authentication.getAuthorities()) {
            if (role.equals(auth.getAuthority()))
                return true;
        }

        return false;
    }
Nate Sammons
sumber
SecurityContextHolder.getContext()tidak pernah NULL, periksa dokumennya. Dengan demikian Anda dapat menghindari memeriksa keberadaan konteks NULL.
Imtiaz Shakil Siddique
14

Anda dapat menerapkan metode hasRole () seperti di bawah ini - (Ini diuji pada keamanan pegas 3.0.x tidak yakin tentang versi lain.)

  protected final boolean hasRole(String role) {
    boolean hasRole = false;
    UserDetails userDetails = getUserDetails();
    if (userDetails != null) {
      Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
      if (isRolePresent(authorities, role)) {
        hasRole = true;
      }
    } 
    return hasRole;
  }
  /**
   * Get info about currently logged in user
   * @return UserDetails if found in the context, null otherwise
   */
  protected UserDetails getUserDetails() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    UserDetails userDetails = null;
    if (principal instanceof UserDetails) {
      userDetails = (UserDetails) principal;
    }
    return userDetails;
  }
  /**
   * Check if a role is present in the authorities of current user
   * @param authorities all authorities assigned to current user
   * @param role required authority
   * @return true if role is present in list of authorities assigned to current user, false otherwise
   */
  private boolean isRolePresent(Collection<GrantedAuthority> authorities, String role) {
    boolean isRolePresent = false;
    for (GrantedAuthority grantedAuthority : authorities) {
      isRolePresent = grantedAuthority.getAuthority().equals(role);
      if (isRolePresent) break;
    }
    return isRolePresent;
  }
Gopi
sumber
1
SecurityContextHolder.getContext().getAuthentication()dapat mengambil null. Mungkin Anda menambahkan beberapa cek?
Mrusful
10

Saya menggunakan ini:

@RequestMapping(method = RequestMethod.GET)
public void welcome(SecurityContextHolderAwareRequestWrapper request) {
    boolean b = request.isUserInRole("ROLE_ADMIN");
    System.out.println("ROLE_ADMIN=" + b);

    boolean c = request.isUserInRole("ROLE_USER");
    System.out.println("ROLE_USER=" + c);
}
Joey Jarin
sumber
8

Anda bisa mendapatkan bantuan dari kelas AuthorityUtils . Memeriksa peran sebagai satu baris:

if (AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities()).contains("ROLE_MANAGER")) {
    /* ... */
}

Peringatan: Ini tidak memeriksa hierarki peran, jika ada.

holmis83
sumber
Itulah solusi paling sederhana karena saya perlu memeriksa Daftar beberapa kali dan mengambilnya sekali dengan rutinitas sederhana ajaib itu hebat!
LeO
6

Jawaban dari JoseK tidak dapat digunakan saat Anda berada di lapisan layanan Anda, di mana Anda tidak ingin memperkenalkan penggabungan dengan lapisan web dari referensi ke permintaan HTTP. Jika Anda ingin menyelesaikan peran saat berada di lapisan layanan, jawaban Gopi adalah cara yang tepat.

Namun, itu agak bertele-tele. Otoritas dapat diakses langsung dari Otentikasi. Oleh karena itu, jika Anda dapat berasumsi bahwa Anda memiliki pengguna yang masuk, berikut ini yang melakukannya:

/**
 * @return true if the user has one of the specified roles.
 */
protected boolean hasRole(String[] roles) {
    boolean result = false;
    for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
        String userRole = authority.getAuthority();
        for (String role : roles) {
            if (role.equals(userRole)) {
                result = true;
                break;
            }
        }

        if (result) {
            break;
        }
    }

    return result;
}
Starman
sumber
6

Sebagian besar jawaban kehilangan beberapa poin:

  1. Peran dan otoritas bukanlah hal yang sama di Spring. Lihat di sini untuk lebih lanjut jelasnya.

  2. Nama peran sama dengan rolePrefix+authority .

  3. ROLE_Namun, awalan peran default dapat dikonfigurasi. Lihat disini .

Oleh karena itu, pemeriksaan peran yang tepat perlu menghormati prefiks peran jika dikonfigurasi.

Sayangnya, kustomisasi peran prefiks di Spring agak hacky, di banyak tempat prefiks default, di ROLE_-hardcode, tetapi selain itu, sejenis kacangGrantedAuthorityDefaults diperiksa dalam konteks Spring, dan jika ada, peran kustom prefiks itu telah dihormati.

Dengan menggabungkan semua informasi ini, implementasi pemeriksa peran yang lebih baik akan menjadi seperti ini:

@Component
public class RoleChecker {

    @Autowired(required = false)
    private GrantedAuthorityDefaults grantedAuthorityDefaults;

    public boolean hasRole(String role) {
        String rolePrefix = grantedAuthorityDefaults != null ? grantedAuthorityDefaults.getRolePrefix() : "ROLE_";
        return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
                .map(Authentication::getAuthorities)
                .map(Collection::stream)
                .orElse(Stream.empty())
                .map(GrantedAuthority::getAuthority)
                .map(authority -> rolePrefix + authority)
                .anyMatch(role::equals);
    }
}
Utku Özdemir
sumber
3

Anehnya, saya rasa tidak ada solusi standar untuk masalah ini, karena kontrol akses keamanan pegas berbasis ekspresi , bukan berbasis java. Anda dapat memeriksa kode sumber untuk DefaultMethodSecurityExpressionHandler untuk melihat apakah Anda dapat menggunakan kembali sesuatu yang mereka lakukan di sana

Sean Patrick Floyd
sumber
Jadi solusi Anda adalah dengan menggunakan DefaultMethodSecurityExpressionHandler sebagai kacang dan mendapatkan pengurai ekspresi dan memeriksanya di EL?
Piotr Gwiazda
yang mungkin tidak akan bekerja, karena penangan beroperasi pada metode invokasi (yang tidak Anda miliki dalam konteks Anda). Anda mungkin perlu membuat kacang Anda sendiri yang melakukan sesuatu yang serupa, tetapi tanpa menggunakan konteks metode panggilan
Sean Patrick Floyd
2

Lebih baik terlambat daripada tidak pernah, biarkan saya memasukkan nilai 2 sen saya.

Di dunia JSF, di dalam kacang yang saya kelola, saya melakukan hal berikut:


HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
SecurityContextHolderAwareRequestWrapper sc = new SecurityContextHolderAwareRequestWrapper(req, "");

Seperti disebutkan di atas, pemahaman saya adalah bahwa hal itu dapat dilakukan dengan cara yang bertele-tele sebagai berikut:


Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetails userDetails = null;
if (principal instanceof UserDetails) {
    userDetails = (UserDetails) principal;
    Collection  authorities = userDetails.getAuthorities();
}
lukec.dll
sumber
2

Ini seperti datang pada pertanyaan dari ujung lain tetapi saya pikir saya akan melemparkannya karena saya benar-benar harus menggali di internet untuk menemukan ini.

Ada banyak hal tentang cara memeriksa peran tetapi tidak banyak mengatakan apa yang sebenarnya Anda periksa ketika Anda mengatakan hasRole ("bla")

HasRole memeriksa otoritas yang diberikan untuk kepala sekolah yang saat ini diautentikasi

Jadi sungguh ketika Anda melihat hasRole ("blah") yang sebenarnya berarti hasAuthority ("blah") .

Dalam kasus yang pernah saya lihat, Anda melakukan ini dengan kelas yang Mengimplementasikan UserDetails yang mendefinisikan metode yang disebut getAuthorities. Dalam hal ini Anda pada dasarnya akan menambahkan beberapanew SimpleGrantedAuthority("some name") ke daftar berdasarkan beberapa logika. Nama-nama dalam daftar ini adalah hal-hal yang diperiksa oleh pernyataan hasRole.

Saya kira dalam konteks ini objek UserDetails adalah kepala sekolah yang saat ini diautentikasi. Ada beberapa keajaiban yang terjadi di dalam dan di sekitar penyedia otentikasi dan lebih khusus lagi manajer otentikasi yang membuat ini terjadi.

JonnyRaa
sumber
2
Pada Keamanan Musim Semi 4.0 ini hasRole("bla")sekarang sama dengan hasAuthority("ROLE_bla").
lanoxx
2

Jawaban @gouki adalah yang terbaik!

Hanya tip bagaimana musim semi benar-benar melakukan ini.

Ada sebuah kelas bernama SecurityContextHolderAwareRequestWrapperyang mengimplementasikan ServletRequestWrapperkelas tersebut.

The SecurityContextHolderAwareRequestWrappermenimpa para isUserInRoledan pencarian pengguna Authentication(yang dikelola oleh musim semi) untuk menemukan jika pengguna memiliki peran atau tidak.

SecurityContextHolderAwareRequestWrapper kodenya adalah sebagai:

    @Override
    public boolean isUserInRole(String role) {
        return isGranted(role);
    }

 private boolean isGranted(String role) {
        Authentication auth = getAuthentication();

        if( rolePrefix != null ) {
            role = rolePrefix + role;
        }

        if ((auth == null) || (auth.getPrincipal() == null)) {
            return false;
        }

        Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();

        if (authorities == null) {
            return false;
        }

        //This is the loop which do actual search
        for (GrantedAuthority grantedAuthority : authorities) {
            if (role.equals(grantedAuthority.getAuthority())) {
                return true;
            }
        }

        return false;
    }
Alireza Fattahi
sumber
2

Kedua anotasi di bawah ini sama, "hasRole" akan otomatis menambahkan awalan "ROLE_". Pastikan Anda memiliki anotasi yang benar. Peran ini disetel di UserDetailsService # loadUserByUsername.

@PreAuthorize("hasAuthority('ROLE_user')")
@PreAuthorize("hasRole('user')")

kemudian, Anda bisa mendapatkan peran dalam kode java.

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_user"))){
    System.out.println("user role2");
}
Luke Cheung
sumber
1

Dalam proyek kami, kami menggunakan hierarki peran, sementara sebagian besar jawaban di atas hanya bertujuan untuk memeriksa peran tertentu, yaitu hanya akan memeriksa peran yang diberikan, tetapi tidak untuk peran tersebut dan hierarki atas.

Solusi untuk ini:

@Component
public class SpringRoleEvaluator {

@Resource(name="roleHierarchy")
private RoleHierarchy roleHierarchy;

public boolean hasRole(String role) {
    UserDetails dt = AuthenticationUtils.getSessionUserDetails();

    for (GrantedAuthority auth: roleHierarchy.getReachableGrantedAuthorities(dt.getAuthorities())) {
        if (auth.toString().equals("ROLE_"+role)) {
            return true;
        }
    }
    return false;
}

RoleHierarchy didefinisikan sebagai kacang di spring-security.xml.

Kirinya
sumber
1
Atau Anda dapat mengisi peran Anda dengan benar: github.com/spring-projects/spring-security/issues/…
arctica
1

Pada model pengguna Anda cukup tambahkan metode 'hasRole' seperti di bawah ini

public boolean hasRole(String auth) {
    for (Role role : roles) {
        if (role.getName().equals(auth)) { return true; }
    }
    return false;
}

Saya biasanya menggunakannya untuk memeriksa apakah pengguna yang diautentikasi memiliki peran admin sebagai berikut

Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // This gets the authentication
User authUser = (User) authentication.getPrincipal(); // This gets the logged in user
authUser.hasRole("ROLE_ADMIN") // This returns true or false
pengguna8174892
sumber
1

Peran Pengguna dapat diperiksa dengan cara berikut:

  1. Menggunakan metode statis panggilan di SecurityContextHolder:

    Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getAuthorities().stream().anyMatch(role -> role.getAuthority().equals("ROLE_NAME"))) { //do something}

  2. Menggunakan HttpServletRequest

@GetMapping("/users")
public String getUsers(HttpServletRequest request) {
    if (request.isUserInRole("ROLE_NAME")) {
      
    }

Beruntung
sumber
0

Pendekatan Saya dengan bantuan Java8, Melewati peran yang dipisahkan koma akan memberi Anda benar atau salah

    public static Boolean hasAnyPermission(String permissions){
    Boolean result = false;
    if(permissions != null && !permissions.isEmpty()){
        String[] rolesArray = permissions.split(",");
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        for (String role : rolesArray) {
            boolean hasUserRole = authentication.getAuthorities().stream().anyMatch(r -> r.getAuthority().equals(role));
            if (hasUserRole) {
                result = true;
                break;
            }
        }
    }
    return result;
}
Jajikanth pydimarla
sumber