จะตรวจสอบ“ hasRole” ใน Java Code ด้วย Spring Security ได้อย่างไร


118

จะตรวจสอบสิทธิ์ของผู้ใช้หรือสิทธิ์ใน Java Code ได้อย่างไร? ตัวอย่างเช่น - ฉันต้องการแสดงหรือซ่อนปุ่มสำหรับผู้ใช้โดยขึ้นอยู่กับบทบาท มีคำอธิบายประกอบเช่น:

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

จะสร้างในโค้ด Java ได้อย่างไร? สิ่งที่ต้องการ :

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

คำตอบ:


70

Spring Security 3.0 มี API นี้

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

คุณจะต้องฉีดกระดาษห่อหุ้มก่อนใช้

SecurityContextHolderAwareRequestWrapper


53
ตามที่คุณตอบไว้ดูเหมือนว่าวิธีนี้จะคงที่ แต่คุณต้องมีSecurityContextHolderAwareRequestWrapperอินสแตนซ์ คุณสามารถปรับปรุงได้โดยอธิบายวิธีการรับและชี้แจงคำตอบให้ชัดเจนขึ้นอีกเล็กน้อย
Xtreme Biker

3
ฉันจะดึง Wrapper ใน Controller ได้อย่างไร?
Alfonso Tienda

3
ฉันจะรับอินสแตนซ์ของ SecurityContextHolderAwareRequestWrapper ได้อย่างไร
gstackoverflow

2
Xtreme Biker ถูกต้องคุณจะได้รับคลาส SecurityContextHolderAwareRequestWrapper ได้อย่างไร ไม่ใช่วัตถุที่หยุดนิ่ง
ทดลองใช้

5
หากนี่เป็นเว็บแอปซึ่งดูเหมือนจะไม่ใช่คุณสามารถเพิ่ม SecurityContextHolderAwareRequestWrapper เป็นพารามิเตอร์ได้ และหากเป็นเว็บแอปคุณสามารถประกาศ HttpServletRequest เป็นพารามิเตอร์และเรียก isUserInRole
David Bradley

144

คุณสามารถใช้เมธอด isUserInRole ของอ็อบเจ็กต์ HttpServletRequest

สิ่งที่ต้องการ:

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


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}

ง่ายต่อการทดสอบผมคิดว่า
fego

1
แต่ถ้าฉันไม่ได้ร้องขอ?
gstackoverflow

แล้ว((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()จะได้อะไร? :)
Petr Újezdský

4
@Autowired คำขอ HttpServletRequest; ?
Pascal

และนั่นไม่ใช่ Spring API, ข้อมูลจำเพาะ Servlet ธรรมดา! น่าเสียดายที่ไม่ใช่คำตอบที่เลือก
gregfqt

67

แทนที่จะใช้ลูปเพื่อค้นหาสิทธิจาก UserDetails คุณสามารถทำได้:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));

2
คำตอบที่ดีกว่ามากอย่างไรก็ตาม ROLE_ADMIN ควรอยู่ในเครื่องหมายคำพูดคู่
Erica Kane

6
สิ่งนี้มีความเสี่ยงมาก โปรดทราบว่าการเปลี่ยนไปใช้การใช้งาน GrantedAuthority อื่น (เช่น JAAS โดยการเพิ่มความเป็นไปได้ในการอนุญาตอื่น) จะทำให้รหัสนี้ทำงานผิดปกติ ดูการใช้งานเท่ากับ () ใน SimpleGrantedAuthority
Petr Újezdský

47

คุณสามารถดึงบริบทความปลอดภัยแล้วใช้สิ่งนั้น:

    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;
    }

SecurityContextHolder.getContext()ไม่เคยNULLตรวจสอบเอกสาร NULLดังนั้นคุณสามารถหลีกเลี่ยงการตรวจสอบบริบทความเป็นอยู่
Imtiaz Shakil Siddique

14

คุณสามารถใช้ hasRole () วิธีการดังต่อไปนี้ - (ทดสอบบน spring security 3.0.x ไม่แน่ใจเกี่ยวกับเวอร์ชันอื่น ๆ )

  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;
  }

1
SecurityContextHolder.getContext().getAuthentication()nullสามารถดึง บางทีคุณอาจจะเพิ่มเช็ค?
Mrusful

10

ฉันใช้สิ่งนี้:

@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);
}

8

คุณสามารถขอความช่วยเหลือจากคลาสAuthorityUtils การตรวจสอบบทบาทเป็นซับเดียว:

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

คำเตือน: สิ่งนี้ไม่ได้ตรวจสอบลำดับชั้นของบทบาทหากมีอยู่


นั่นเป็นวิธีแก้ปัญหาที่ง่ายที่สุดเพราะฉันต้องตรวจสอบรายชื่อหลาย ๆ ครั้งและดึงข้อมูลหนึ่งครั้งด้วยกิจวัตรที่เรียบง่ายอย่างมหัศจรรย์นั้นยอดเยี่ยมมาก!
LeO

6

ไม่สามารถใช้คำตอบจาก JoseK เมื่อคุณอยู่ในชั้นบริการของคุณซึ่งคุณไม่ต้องการแนะนำการเชื่อมต่อกับเลเยอร์เว็บจากการอ้างอิงไปยังคำขอ HTTP หากคุณกำลังมองหาการแก้ไขบทบาทขณะที่อยู่ในชั้นบริการคำตอบของ Gopi คือหนทางที่จะไป

อย่างไรก็ตามมันยาวไปหน่อย เจ้าหน้าที่สามารถเข้าถึงได้จากการรับรองความถูกต้อง ดังนั้นหากคุณสามารถสันนิษฐานได้ว่าคุณมีผู้ใช้เข้าสู่ระบบสิ่งต่อไปนี้จะทำ:

/**
 * @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;
}

6

คำตอบส่วนใหญ่ไม่มีบางประเด็น:

  1. บทบาทและอำนาจไม่ใช่สิ่งเดียวกันในฤดูใบไม้ผลิ ดูที่นี่สำหรับเพิ่มเติมรายละเอียด

  2. ชื่อบทบาทจะเท่ากับ+rolePrefixauthority

  3. อย่างไรก็ตามคำนำหน้าบทบาทเริ่มต้นROLE_นั้นสามารถกำหนดค่าได้ ดูที่นี่ .

ดังนั้นการตรวจสอบบทบาทที่เหมาะสมจำเป็นต้องเคารพคำนำหน้าบทบาทหากมีการกำหนดค่าไว้

น่าเสียดายที่การปรับแต่งคำนำหน้าบทบาทใน Spring นั้นค่อนข้างแฮ็กในหลาย ๆ ที่คำนำหน้าเริ่มต้นนั้นROLE_ถูกเข้ารหัส แต่นอกจากนั้นแล้วยังGrantedAuthorityDefaultsมีการตรวจสอบประเภท bean ในบริบท Spring และหากมีอยู่คำนำหน้าบทบาทที่กำหนดเองจะเป็นคำนำหน้า ได้รับความเคารพ

การนำข้อมูลทั้งหมดนี้มารวมกันการใช้งานตัวตรวจสอบบทบาทที่ดีขึ้นจะเป็นดังนี้:

@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);
    }
}

3

น่าแปลกที่ฉันไม่คิดว่าจะมีวิธีแก้ปัญหามาตรฐานสำหรับปัญหานี้เนื่องจากการควบคุมการเข้าถึงความปลอดภัยในฤดูใบไม้ผลินั้นขึ้นอยู่กับนิพจน์ไม่ใช่แบบจาวา คุณอาจตรวจสอบซอร์สโค้ดสำหรับ DefaultMethodSecurityExpressionHandlerเพื่อดูว่าคุณสามารถใช้สิ่งที่พวกเขาทำอยู่ที่นั่นซ้ำได้หรือไม่


ดังนั้นวิธีแก้ปัญหาของคุณคือใช้ DefaultMethodSecurityExpressionHandler เป็น bean และรับ expression parser แล้วตรวจสอบใน EL?
Piotr Gwiazda

ซึ่งอาจไม่ได้ผลเนื่องจากตัวจัดการดำเนินการกับ methodinvocations (ซึ่งคุณไม่มีในบริบทของคุณ) คุณอาจต้องสร้าง bean ของคุณเองที่ทำสิ่งที่คล้ายกัน แต่ไม่ต้องใช้บริบทวิธีการจัดตำแหน่ง
Sean Patrick Floyd

2

ดีกว่าช้ากว่าไม่เคยให้ฉันใส่มูลค่า 2 เซ็นต์ของฉัน

ในโลก JSF ภายในถั่วที่มีการจัดการของฉันฉันทำสิ่งต่อไปนี้:


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

ดังที่ได้กล่าวมาแล้วความเข้าใจของฉันคือสามารถทำได้ด้วยวิธีที่ยืดยาวดังนี้:


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

2

นี่เป็นคำถามที่มาจากอีกด้านหนึ่ง แต่ฉันคิดว่าฉันจะโยนมันลงไปเพราะฉันต้องขุดบนอินเทอร์เน็ตเพื่อหาสิ่งนี้

มีหลายอย่างเกี่ยวกับวิธีตรวจสอบบทบาท แต่ไม่ค่อยบอกว่าคุณกำลังตรวจสอบอะไรอยู่เมื่อคุณพูดว่า hasRole ("blah")

HasRole ตรวจสอบหน่วยงานที่ได้รับอนุญาตสำหรับหลักที่ได้รับการรับรองความถูกต้องในปัจจุบัน

ดังนั้นเมื่อคุณเห็นhasRole ("blah") แปลว่า hasAuthority ("blah")จริงๆ

ในกรณีที่ฉันเคยเห็นคุณทำสิ่งนี้กับคลาสที่ Implements UserDetails ซึ่งกำหนดเมธอดที่เรียกว่า getAuthorities ในนี้คุณจะเพิ่มบางส่วนnew SimpleGrantedAuthority("some name")ในรายการตามตรรกะบางอย่าง ชื่อในรายการนี้เป็นสิ่งที่ตรวจสอบโดยคำสั่ง hasRole

ฉันเดาว่าในบริบทนี้ออบเจ็กต์ UserDetails คือหลักการรับรองความถูกต้องในปัจจุบัน มีเวทมนตร์บางอย่างที่เกิดขึ้นในและรอบ ๆ ผู้ให้บริการการตรวจสอบสิทธิ์และโดยเฉพาะอย่างยิ่งผู้จัดการการตรวจสอบสิทธิ์ที่ทำให้สิ่งนี้เกิดขึ้น


2
ขณะที่การรักษาความปลอดภัยของฤดูใบไม้ผลิ 4.0 นี้คือตอนนี้เท่ากับhasRole("bla") hasAuthority("ROLE_bla")
lanoxx

2

คำตอบของ @gouki ดีที่สุด!

เพียงแค่เคล็ดลับว่าสปริงทำสิ่งนี้ได้อย่างไร

มีคลาสที่ตั้งชื่อSecurityContextHolderAwareRequestWrapperซึ่งใช้ServletRequestWrapperคลาส

SecurityContextHolderAwareRequestWrapperแทนที่isUserInRoleและค้นหาผู้ใช้Authentication(ซึ่งมีการจัดการโดยฤดูใบไม้ผลิ) เพื่อหาถ้าผู้ใช้มีบทบาทหรือไม่

SecurityContextHolderAwareRequestWrapper รหัสเป็น:

    @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;
    }

2

คำอธิบายประกอบสองคำด้านล่างนี้เท่ากัน "hasRole" จะเพิ่มคำนำหน้า "ROLE_" โดยอัตโนมัติ ตรวจสอบว่าคุณมีคำอธิบายประกอบที่ถูกต้อง บทบาทนี้ถูกตั้งค่าใน UserDetailsService # loadUserByUsername

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

จากนั้นคุณจะได้รับบทบาทในรหัส java

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

1

ในโครงการของเราเรากำลังใช้ลำดับชั้นของบทบาทในขณะที่คำตอบข้างต้นส่วนใหญ่มุ่งเป้าไปที่การตรวจสอบเฉพาะบทบาทเท่านั้นกล่าวคือจะตรวจสอบเฉพาะบทบาทที่กำหนดเท่านั้น แต่ไม่ใช่สำหรับบทบาทนั้นและลำดับชั้น

ทางออกสำหรับสิ่งนี้:

@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 ถูกกำหนดให้เป็น bean ใน spring-security.xml


1
หรือคุณสามารถเติมเต็มบทบาทของคุณได้อย่างเหมาะสม: github.com/spring-projects/spring-security/issues/…
arctica

1

ในรุ่นผู้ใช้ของคุณเพียงแค่เพิ่ม 'hasRole' วิธีการดังต่อไปนี้

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

ฉันมักจะใช้เพื่อตรวจสอบว่าผู้ใช้ที่ได้รับการรับรองความถูกต้องมีบทบาทผู้ดูแลระบบดังนี้

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

1

สามารถตรวจสอบบทบาทของผู้ใช้ได้โดยใช้วิธีต่อไปนี้:

  1. ใช้วิธีการโทรแบบคงที่ใน SecurityContextHolder:

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

  2. ใช้ HttpServletRequest

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


0

วิธีการของฉันด้วยความช่วยเหลือของ Java8 การผ่านการแยกบทบาทโคม่าจะทำให้คุณเป็นจริงหรือเท็จ

    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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.