如何使用 Spring 安全检查 Java 代码中的“ hasRole”?

如何在 Java 代码中检查用户权限?例如-我想显示或隐藏按钮的用户取决于角色。有这样的注释:

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

如何在 Java 代码中实现? 比如:

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

奇怪的是,我不认为这个问题有一个标准的解决方案,因为弹簧安全访问控制是 基于表达式的,而不是基于 Java 的。您可以检查源代码 DefaultMethodSecurityExpressionHandler 查看是否可以重用他们正在做的事情

Spring Security 3.0有这个 API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

在使用之前,你必须注射包装纸。

SecurityContextHolderAwareRequestWrapper

您可以使用 HttpServletRequest 对象的 isUserInRole 方法。

比如:

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




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

您可以实现 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;
}

迟到总比不到好,让我说说我的价值。

在 JSF 世界中,在我的托管 bean 中,我执行了以下操作:


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

JoseK 给出的答案不能在服务层中使用,因为在服务层中,您不希望引入从引用到 HTTP 请求的 Web 层耦合。如果您正在寻找解决服务层中的角色,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;
}

我用这个:

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

您可以检索安全上下文,然后使用:

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

不要使用循环查找 UserDetails 的授权,你可以这样做:

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

这个问题从另一个角度出发,但是我觉得我应该把它放进去,因为我真的需要在互联网上挖掘才能找到答案。

有很多关于如何检查角色的东西,但是当你说 hasRole (“等等”)时,并没有说明你实际检查的是什么

HasRole 检查授予的当前已验证主体的权限

所以当你看到 hasRole (“巴拉”)实际上是 hasAuthority (“巴拉”)的意思的时候。

在我看到的例子中,您可以使用一个类来实现 UserDetails,该类定义一个名为 getAuthority 的方法。在这里,您基本上将根据一些逻辑向列表中添加一些 new SimpleGrantedAuthority("some name")。此列表中的名称是 hasRole 语句检查的内容。

我猜在这种情况下,UserDetails 对象是当前经过身份验证的主体。在身份验证提供程序内部和周围发生了一些神奇的事情,更具体地说,是身份验证管理器使这种情况发生的。

@ gouki 的答案是最好的!

只是春天如何真正做到这一点的一个提示。

有一个名为 SecurityContextHolderAwareRequestWrapper的类实现了 ServletRequestWrapper类。

SecurityContextHolderAwareRequestWrapper覆盖 isUserInRole并搜索用户 Authentication(由 Spring 管理)以查找用户是否具有角色。

代码如下:

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

在我们的项目中,我们使用了一个角色层次结构,而上面的大多数答案只是针对一个特定的角色进行检查,也就是说,只检查给定的角色,而不是该角色和层次结构。

解决这个问题的办法是:

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

在 spring-security.xml 中,RoleHiLayer y 被定义为一个 bean。

你可以从 授权实用程序课程中得到一些帮助:

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

警告: 如果存在角色层次结构,这不会检查它。

大多数答案都漏掉了一些要点:

  1. 在 Spring 中,角色和权限不是一回事。

  2. 角色名等于 rolePrefix + authority

  3. 默认的角色前缀是 ROLE_,但是它是可配置的。

因此,正确的角色检查需要尊重配置的角色前缀。

不幸的是,Spring 中的角色前缀定制有点古怪,在许多地方,默认前缀 ROLE_是硬编码的,但除此之外,在 Spring 上下文中检查类型为 GrantedAuthorityDefaults的 bean,如果存在,则尊重它所具有的自定义角色前缀。

将所有这些信息放在一起,一个更好的角色检查器实现应该是这样的:

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

我的方法在 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;
}

下面的两个注释是相等的,“ 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");
}

在您的用户模型上,只需像下面这样添加一个“ hasRole”方法

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

我通常使用它来检查经过身份验证的用户是否具有如下角色 admin

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. 在 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")) {
      

}

您可以在 Spring Security 中使用注释 @Secured@RolesAllowed@PreAuthorise/@PostAuthorise

记住: 您需要添加此代码

@Configuration
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class MyConfig extends WebSecurityConfigurerAdapter{
}

在您的 configure 类的前面。你不需要使用所有的3个参数 securedEnabledjsr250EnabledprePostEnabled。您只需要一个注释,具体取决于要使用哪个注释。

然后将角色检查注释放到控制器类中。

@Secured("ROLE_admin")
@GetMapping("/hello")
public String hello(){
return "hello";
}

或者

@RolesAllowed("ROLE_admin")
@GetMapping("/hello")
public String hello(){
return "hello";
}

或者

@PreAuthorize("hasRole('ROLE_user')")
@GetMapping("/hello")
public String hello(){
return "hello";
}

这里是一个教程 https://www.baeldung.com/spring-security-method-security