GVKun编程网logo

Spring Security中角色和GrantedAuthority之间的区别(spring security 角色)

12

如果您对SpringSecurity中角色和GrantedAuthority之间的区别和springsecurity角色感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解SpringSecurit

如果您对Spring Security中角色和GrantedAuthority之间的区别spring security 角色感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解Spring Security中角色和GrantedAuthority之间的区别的各种细节,并对spring security 角色进行深入的分析,此外还有关于25.SpringSecurity-SpringSecurityOAuth核心源码解析、33.SpringSecurity-SpringSecurity Oauth授权源码解读、@PreAuthorize批注在Spring Security中不起作用、Cannot construct instance of `org.springframework.security.core.authority.SimpleGrantedAuthority`的实用技巧。

本文目录一览:

Spring Security中角色和GrantedAuthority之间的区别(spring security 角色)

Spring Security中角色和GrantedAuthority之间的区别(spring security 角色)

如何解决Spring Security中角色和GrantedAuthority之间的区别?

将GrantedAuthority视为“权限”或“权利”。这些“权限”(通常)用字符串表示(使用getAuthority()方法)。这些字符串使你可以标识权限,并让你的选民决定他们是否授予对某些内容的访问权限。

你可以通过将用户置于安全上下文中来为其授予不同的GrantedAuthority(权限)。通常,你可以通过实现自己的UserDetailsS​​ervice来实现此目的,该服务返回一个UserDetails实现,该实现返回所需的GrantedAuthorities。

角色(在许多示例中使用过)只是“权限”,使用命名约定表示角色是以prefix开头的GrantedAuthority ROLE_。没什么了 一个角色就是一个GrantedAuthority-一个“权限”-一个“权利”。你会在Spring Security中看到很多地方,带有ROLE_前缀的角色是专门处理的,例如在RoleVoter中,ROLE_前缀被用作默认值。这样,你就可以提供角色名称而无需添加ROLE_前缀。在Spring安全4之前,这种对“角色”的特殊处理并未得到非常一致的遵循,并且权限和角色通常被视为相同(例如hasAuthority()hasRole())。使用Spring Security 4,角色的处理更加一致,处理“角色”的代码(如RoleVoter,hasRole表达式等)总是ROLE_为你添加前缀。所以hasAuthority(''ROLE_ADMIN'')指一样hasRole(''ADMIN''),因为ROLE_前缀被自动添加。有关更多信息,请参见Spring Security 3到4 迁移指南。

但是,角色仍然只是带有特殊ROLE_前缀的授权机构。因此在Spring安全性3 @PreAuthorize("hasRole(''ROLE_XYZ'')")中与相同@PreAuthorize("hasAuthority(''ROLE_XYZ'')"),在Spring安全性4 @PreAuthorize("hasRole(''XYZ'')")中与相同@PreAuthorize("hasAuthority(''ROLE_XYZ'')")

关于你的用例:

用户具有角色,角色可以执行某些操作。

你可能最终会GrantedAuthorities获得用户所属的角色以及角色可以执行的操作。在GrantedAuthorities对角色有前缀ROLE_和操作都有前缀OP_。一个例子为业务主管部门可能是OP_DELETE_ACCOUNT,OP_CREATE_USER,OP_RUN_BATCH_JOB等角色可以是ROLE_ADMIN,ROLE_USER,ROLE_OWNER等。

你最终可能会GrantedAuthority像下面的(伪代码)示例中那样使你的实体实现:

@Entity
class Role implements GrantedAuthority {
    @Id
    private String id;

    @ManyToMany
    private final List<Operation> allowedOperations = new ArrayList<>();

    @Override
    public String getAuthority() {
        return id;
    }

    public Collection<GrantedAuthority> getAllowedOperations() {
        return allowedOperations;
    }
}

@Entity
class User {
    @Id
    private String id;

    @ManyToMany
    private final List<Role> roles = new ArrayList<>();

    public Collection<Role> getRoles() {
        return roles;
    }
}

@Entity
class Operation implements GrantedAuthority {
    @Id
    private String id;

    @Override
    public String getAuthority() {
        return id;
    }
}

你在数据库中创建的角色和操作的ID将是GrantedAuthority表示形式,例如ROLE_ADMIN,OP_DELETE_ACCOUNT等等。对用户进行身份验证时,请确保从UserDetails.getAuthorities()返回了其所有角色的所有GrantedAuthority和相应的操作方法。

例如:ID为admin角色ROLE_ADMIN有操作OP_DELETE_ACCOUNT,OP_READ_ACCOUNT,OP_RUN_BATCH_JOB分配给它。ID为的用户角色具有ROLE_USER操作OP_READ_ACCOUNT

如果造成安全上下文管理员日志将有GrantedAuthorities:ROLE_ADMIN,,OP_DELETE_ACCOUNTOP_READ_ACCOUNTOP_RUN_BATCH_JOB

如果一个用户登录它,它就会有: ROLE_USER,OP_READ_ACCOUNT

UserDetailsS​​ervice将注意收集所有角色以及这些角色的所有操作,并通过返回的UserDetails实例中的方法getAuthorities()使它们可用。

解决方法

Spring Security中有一些概念和实现,例如GrantedAuthority用于获得授权来授权/控制访问权限的接口。

我希望对允许的操作(例如createSubUsersdeleteAccounts)进行允许,这些操作将允许管理员(具有role ROLE_ADMIN)使用。

在网上看到的教程/演示让我感到困惑。我尝试将我阅读的内容联系起来,但我认为我们可以将两者互换。

我看到正在hasRole消耗GrantedAuthority字符串吗?我肯定在理解上做错了。Spring Security中的这些概念是什么?

如何将用户角色与角色的权限分开存储?

我还查看org.springframework.security.core.userdetails.UserDetails了身份验证提供程序引用的DAO中使用的接口,该接口消耗了User(请注意最后的GrantedAuthority):

public User(String username,String password,boolean enabled,boolean accountNonExpired,boolean credentialsNonExpired,boolean accountNonLocked,Collection<? extends GrantedAuthority> authorities)

还是有其他方法可以区分其他两个?还是不受支持,我们必须自己做?

25.SpringSecurity-SpringSecurityOAuth核心源码解析

25.SpringSecurity-SpringSecurityOAuth核心源码解析

前言

image.png

  1. 之前我们已经完成了上面的服务提供商中:认证服务器、资源服务器的开发。 实际上代码非常简单,就是使用Spring Security OAuth的注解。 有了这些注解后我们已经可以按照标准的OAuth协议往外边发访问令牌access_token。上节演示了如何用授权码模式,密码模式获取access_token。然后拿着这些令牌去访问我们的资源服务器中的资源。
  2. 以上标准流程做完之后,我们接下来需要做的就是把我们之前做的三种模式(短信登录、QQ登录、微信登录)嫁接到标准模式中去。让我们在这三种模式认证通过之后。也可以返回access_token。那么为了实现这个目的,我们需要实现上面这个源码。

内容

1.Spring Security Oauth核心源码

image.png

  1. 上图中:绿色方块表示实体类,蓝色方块表示接口,括号中为真正实现类。
  2. TokenEndpoint是程序入口点,可以理解成一个controller,他会处理令牌请求。上一节中我们用授权码模式、密码模式时候,请求的url都是一致的,他是通过grant\_type来告知我们使用的是哪种请求模式。他处理/oauth/token请求。当收到令牌请求时候,TokenEndPoint会调用我们的ClientDetailsService
  3. ClientDetailsService区别于UserDetailsService;UserDetailsService是读取用户信息的,ClientDetailsService用来读取第三方应用信息的。我们之前发送请求时候在Header里面都会携带clientId和clientSecret来告诉是哪个应用请求授权,这个ClientDetailsService就会根据传过来的clientId和clientSecret读取响应的client配置信息。这些配置信息都会读取到ClientDetails这个对象里面去。
  4. ClientDetails:这里面封装的是第三方应用的信息,然后TokenEndpoint还会创建一个TokenRequest对象。
  5. TokenRequest:封装了我们请求的其他参数信息,比如:grant_type;如果是密码模式的话,我们的用户名、密码是什么?同时也会把ClientDetails信息放到TokenRequest里面去。因为第三方应用信息 也是令牌请求一部分。利用TokenRequest会去调用我们的TokenGranter(令牌授权者)接口,这个接口后面其实封装了我们的4种授权模式的不同实现。这个接口里面会根据你接口传上来的grant_type去挑选一个自己的实现去执行令牌的生成。不管是哪种实现,生成过程中都会产生2种东西:OAuth2Request和Authentication。
  6. OAuth2Request其实就是之前ClientDetails和TokenRequest信息的整合。
  7. Authentication接口封装了当前授权用户的一些信息。谁在做一些授权,授权用户信息就是在Authentication接口里面。它里面的信息是我们通过UserDetailsService读出来的。
  8. OAuth2Authentication:OAuth2Request和Authentication整合形成。它里面包含了现在是哪一个第三方应用在请求哪个用户在授权,然后用的授权模式是什么?授权参数是什么等?然后会传递给接口:AuthorizationServerTokenServices
  9. AuthorizationServerTokenServices拿到我们OAuth2Authentication之后,他会最终生成一个OAuth2AccessToken令牌。AuthorizationServerTokenServices的默认实现的:DefaultTokenServices里面含有两个接口的引用:TokenStore和TokenEnhancer
  10. TokenStore用来处理令牌存储。
  11. TokenEnhancer令牌生成器,当我们令牌生成之后,我们可以自定义去改造令牌。

2.源码追踪

我们使用密码模式追踪。因为授权码模式需要两步(第一步获取授权码,然后拿着授权码去获取token),测试起来比较麻烦。

image.png

然后生成TokenRequest:
image.png

//根据请求参数和客户端信息创建tokenRequest  
TokenRequest tokenRequest = new TokenRequest(requestParameters, clientId, scopes, grantType);
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
        } else {
            
            String clientId = this.getClientId(principal);
            //1.获取三方授权信息  
            ClientDetails authenticatedClient = this.getClientDetailsService().loadClientByClientId(clientId);
            
            //2.根据三方授权信息和参数生成TokenRequest
            TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
            
            //3.参数校验 
            if (clientId != null && !clientId.equals("") && !clientId.equals(tokenRequest.getClientId())) {
                throw new InvalidClientException("Given client ID does not match authenticated client");
            } else {
                if (authenticatedClient != null) {
                    this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
                }

                if (!StringUtils.hasText(tokenRequest.getGrantType())) {
                    throw new InvalidRequestException("Missing grant type");
                    //简化模式:是在用户授权时候直接返回令牌,不会存在请求令牌服务被调用
                } else if (tokenRequest.getGrantType().equals("implicit")) {
                    throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
                } else {
                //判断是否是授权码模式的请求:我们需要重新设置scope
                    if (this.isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) {
                        this.logger.debug("Clearing scope of incoming token request");
                        tokenRequest.setScope(Collections.emptySet());
                    }
                  //如果是刷新令牌的请求,我们需要重新设置scope
                    if (this.isRefreshTokenRequest(parameters)) {
                        tokenRequest.setScope(OAuth2Utils.parseParameterList((String)parameters.get("scope")));
                    }

                   //4.根据TokenGranter创建 OAuth2AccessToken
                    OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
                    if (token == null) {
                        throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
                    } else {
                        return this.getResponse(token);
                    }
                }
            }
        }
    }

我们进入TokenGranter其实现类:CompositeTokenGranter里面:

image.png

里面会存在包含refresh\_token的5种模式,一次遍历找到对应目前人的授权。

然后根据传入的grantType参数,找到对应的授权类型,生成: ,然后返回回去,产生最终令牌:

密码模式的实现:
image.png

然后进入:我们使用的:ResourceOwnerPasswordTokenGranter

image.png
image.png

此时会交给 private final AuthenticationManager authenticationManager 去管理生成:Authentication 此时调用的是:ProviderManager,然后将生成的Authentication作为参数传递给:OAuth2Authentication生成一个OAuth2Authentication返回。

image.png

然后通过AbstractTokenGranter生成:OAuth2AccessToken
image.png

3.DefaultTokenServices详解

DefaultTokenServices如何把OAuth2AcessToken生成出来的?我们进入DefaultTokenServices中,在如下地方打断点。我们去掉其他断点,然后重新发送请求。

@Transactional
    public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
    
    //1.tokenStore去查找是否之前的access_token过期  
        OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(authentication);
        OAuth2RefreshToken refreshToken = null;
        if (existingAccessToken != null) {
        
            //Access_Token没有过期,因为有可能之前是授权码模式,现在是用户名/密码模式;所以重新存储覆盖:access_token并且返回access_token
            if (!existingAccessToken.isExpired()) {
                this.tokenStore.storeAccessToken(existingAccessToken, authentication);
                return existingAccessToken;
            }

            if (existingAccessToken.getRefreshToken() != null) {
                refreshToken = existingAccessToken.getRefreshToken();
                this.tokenStore.removeRefreshToken(refreshToken);
            }

            this.tokenStore.removeAccessToken(existingAccessToken);
        }

        //2.没有找到之前存储的access_token,所以我们查找:refreshToken没有的话就生成:refreshToken
        if (refreshToken == null) {
            refreshToken = this.createRefreshToken(authentication);
        } else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
            ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken)refreshToken;
            if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
                refreshToken = this.createRefreshToken(authentication);
            }
        }

        //3.我们根据RefreshToken和OAuth2Authentication创建一个新的OAuth2AccessToken和refreshToken存储起来。  
        OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken);
        this.tokenStore.storeAccessToken(accessToken, authentication);
        refreshToken = accessToken.getRefreshToken();
        if (refreshToken != null) {
            this.tokenStore.storeRefreshToken(refreshToken, authentication);
        }

        return accessToken;
    }

创建信息OAuth2AccessToken的时候会调用增强来个性化自己的OAuth2AccessToken

private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
        DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
        int validitySeconds = this.getAccessTokenValiditySeconds(authentication.getOAuth2Request());
        if (validitySeconds > 0) {
            token.setExpiration(new Date(System.currentTimeMillis() + (long)validitySeconds * 1000L));
        }

        token.setRefreshToken(refreshToken);
        token.setScope(authentication.getOAuth2Request().getScope());
        
        //1.使用accessTokenEnhancer增强生成OAuth2AccessToken  
        return (OAuth2AccessToken)(this.accessTokenEnhancer != null ? this.accessTokenEnhancer.enhance(token, authentication) : token);
}

33.SpringSecurity-SpringSecurity Oauth授权源码解读

33.SpringSecurity-SpringSecurity Oauth授权源码解读

前言

image.png

  1. 我们前几节讲解Spring Security的时候,核心原理就是上图所示的过滤器链路。
  2. 我们核心考察的是类:FilterSecurityInterceptor;FilterSecurityInterceptor用于最后校验我们的请求是否最后能够到达我们的REST API.如果不能的话,会抛出异常,抛出异常的话,会由于其前面的ExceptionTranslationFilter处理。
  3. 我们这里与权限相关的主要类是:FilterSecurityInterceptor和ExceptionTranslationFilter
  4. 我们之前讲解的各种过滤器,现在我们主要讲解的是:AnonymousAuthenticationFilter(匿名认证过滤器),他处于我们上面绿色过滤器最后,不管前面有各种过滤器,后面都会到AnonymousAuthenticationFilter(匿名认证过滤器):看代码之后,主要看其里面的逻辑是判断当前SecurityContextHolder里面是否有:Authentication;也就是说:前面过滤器是否成功进行了成功地身份认证。

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
             if (SecurityContextHolder.getContext().getAuthentication() == null) {
                 SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));
                 if (this.logger.isDebugEnabled()) {
                     this.logger.debug("Populated SecurityContextHolder with anonymous token: ''" + SecurityContextHolder.getContext().getAuthentication() + "''");
                 }
             } else if (this.logger.isDebugEnabled()) {
                 this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: ''" + SecurityContextHolder.getContext().getAuthentication() + "''");
             }
    
             chain.doFilter(req, res);
         }
  5. 我们之前说过,认证成功之后,我们返回的是一个:Authentication.我们查看:AnonymousAuthenticationFilter的创建:Authentication 我们知道:里面的用户信息:principal是:anonymousUser,如果前面的过滤器没有认证成功,那么此时 SecurityContextHolder.getContext()的认证信息:Authentication就是AnonymousAuthenticationFilter自己创建的。

     protected Authentication createAuthentication(HttpServletRequest request) {
             AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(this.key, this.principal, this.authorities);
             auth.setDetails(this.authenticationDetailsSource.buildDetails(request));
             return auth;
         }
    
     public AnonymousAuthenticationFilter(String key) {
             this(key, "anonymousUser", AuthorityUtils.createAuthorityList(new String[]{"ROLE_ANONYMOUS"}));
         }
    
     public AnonymousAuthenticationFilter(String key, Object principal, List<GrantedAuthority> authorities) {
         this.authenticationDetailsSource = new WebAuthenticationDetailsSource();
         Assert.hasLength(key, "key cannot be null or empty");
         Assert.notNull(principal, "Anonymous authentication principal must be set");
         Assert.notNull(authorities, "Anonymous authorities must be set");
         this.key = key;
         this.principal = principal;
         this.authorities = authorities;
     }
  6. 所以通过上面分析我们知道:如果我们认证成功,那么返回的Authentication里面的principal就是我们UserDetailsService里面封装的UserDetails信息,如果没有认证成功,那么Authentication里面的principa就是上面的"anonymousUser"字符串。
  7. 不管怎样,最后传给我们FilterSecurityInterceptor的Authentication都会存在,然后FilterSecurityInterceptor决定当前包含的Authentication包含的权限是否可以访问你当前请求的url。

内容

1.SpringSecurity授权逻辑图分析

image.png

我们现在主要关注:FilterSecurityInterceptor和ExceptionTranslationFilter,首先我们观察下spring security中和授权相关的类接口以及调用关系。

其中核心的类和接口就只有3个:
FilterSecurityInterceptor、AccessDecisionManager、AccessDecisionVoter。

  1. FilterSecurityInterceptor是我们Spring Security过滤器链路上最后的拦截器,是我们授权的主入口,FilterSecurityInterceptor其实是一个Filter
  2. AccessDecisionManager(访问决定管理器),其实是一个接口,他有一个抽象的实现:AbstractAccessDecisionManager和3个具体类;他是一个管理者,管理的是什么了?从名字我们可以看到,他管理的是一组:AccessDecisionVoter(授权决定投票者)
  3. AccessDecisionVoter会综合所有投票者的投票结果,然后给出一个最终结果过还是不过,具体判断过与不过,有3套这样的逻辑,具体的投票逻辑是在AbstractAccessDecisionManager的子类里面:
    a.AffirmativeBased:只有有一个voter投通过,那么整体请求就通过。 b.ConsensusBased:比较投通过和不通过的票数, 哪一种意见多就用哪一种。
    c.UnanimousBased:不管有多少个voter投通过,只要有一个投不通过。整个请求不通过。默认spring security默认使用第一种:AffirmativeBased
  4. 那么SecurityConfig和SecurityContextHolder是干什么的呢?我们之前说要判断一个请求是否能够正常访问?需要两方面的数据:a.系统配置信息(具体url需要什么样的信息),这些配置信息我们是配置到:
    image.png
    FilterSecurityInterceptor会从我们的安全配置:SecurityConfig里面信息读出来,封装成一组SecurityAttribute这样的一组对象,这组对象里面其实每一个SecurityAttribute对应着一个url所对应的权限。这里面其实就是你系统配置的信息。
    Authentication信息封装的基本认证信息。
  5. Authentication、ConfigAttribute和FilterSecurityInterceptor携带的请求信息一起传给我们的AccessDecisionManager,然后AccessDecisionManager交给 投票者,由投票者来投票是过还是不过。

2.SpringSecurity源码追踪

我们看两个流程:一个是:用户没登录时候,被拒绝访问流程,另一个是用户登录以后,访问通过流程。

2.1 用户没登录时候,被拒绝访问流程

我们访问:http://127.0.0.1:8088/
断点:FilterSecurityInterceptor
image.png

//封装请求、响应、过滤器链到FilterInvocation
FilterInvocation fi = new FilterInvocation(request, response, chain);
//判断:此请求是否经过FilterSecurityInterceptor处理,经过的话直接返回到下一个Filter中。  
 if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
//处理授权的核心逻辑,就是我们FilterSecurityInterceptor判断是否可以访问RESTAPI,授权主要也在这里,如果授权不通过,则会抛出异常。    
InterceptorStatusToken token = super.beforeInvocation(fi);

protected InterceptorStatusToken beforeInvocation(Object object)方法中

//读取我们的SecurityConfig将其封装成一个对象。
 Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

DefaultFilterInvocationSecurityMetadataSource:

image.png
我们可以看到每一个url,所具备的权限。

这个map就是根据我们WebSecurityConfig里面protected void configure(HttpSecurity http) throws Exception()生成的。

//判断SecurityContextHolder.getContext()里面是否有授权信息,因为授权信息我们就算没有认证成功,也会在AnonymousAuthenticationFilter生成,如果确实为空,则有异常了。  
if (SecurityContextHolder.getContext().getAuthentication() == null) {
                    this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
                }

image.png

image.png

通过decide方法决定是不是让其通过.
image.png

image.png

未有授权通过就会抛出异常:异常先从:最先调用的AffirmativeBased抛出异常,然后抛出异常到父类,再次到接口,最后抛出异常到: FilterSecurityInterceptor;然后往前抛到FilterSecurityInterceptor前面的异常转换过滤器(ExceptionTranslationFilter);

image.png

然后进入: this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);

image.png

把我发出去去认证。
image.png
image.png
把我发出去去认证,其实就是跳到了我们之前在:WebSecurityConfig配置里面loginPage所配置的。
image.png
image.png

image.png

2.1 用户没登录时候,被拒绝访问流程

之前是用户未登录时候的授权流程,我们现在开始登陆。
image.png

登录完成之后,我们再次访问:

http://127.0.0.1:8088/user/1

image.png

这个hasRole(''ROLE_ADMIN'')是怎么来的呢?
我们在配置文件总配置了:ADMIN
image.png

然后进入源码跟踪:ExpressionUrlAuthorizationConfigurer的:

public ExpressionUrlAuthorizationConfigurer<H>.ExpressionInterceptUrlRegistry hasRole(String role) {
    return this.access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
}

然后进入:ExpressionUrlAuthorizationConfigurer

private static String hasRole(String role) {
    Assert.notNull(role, "role cannot be null");
    if (role.startsWith("ROLE_")) {
        throw new IllegalArgumentException("role should not start with ''ROLE_'' since it is automatically inserted. Got ''" + role + "''");
    } else {
        return "hasRole(''ROLE_" + role + "'')";
    }
}

说明返回的字符串会拼接前缀返回:"hasRole(''ROLE_" + role + "'')"。

所以我们授权时候也需要给:ROLE_ADMIN

image.png

上面我们拿到了系统配置和Authentication信息,现在我们进入投票Voter,结果如下:

image.png
image.png

最后拿到了我们的接口返回信息:
image.png

@PreAuthorize批注在Spring Security中不起作用

@PreAuthorize批注在Spring Security中不起作用

我发现了许多类似的问题,但都没有解决我的问题。我的问题是ROLE_USER可以访问的功能ROLE_ADMIN

我的spring-security.xml代码如下。

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:s="http://www.springframework.org/schema/security"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                    http://www.springframework.org/schema/security
                    http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<s:http auto-config="true" use-expressions="true">
    <s:intercept-url pattern="/index.jsp" access="permitAll" />
    <s:intercept-url pattern="/welcome*" access="hasRole('ROLE_USER')" />
    <s:intercept-url pattern="/helloadmin*" access="hasRole('ROLE_ADMIN')" />

    <s:form-login login-page="/login" default-target-url="/welcome"
        authentication-failure-url="/loginfailed" />
    <s:logout logout-success-url="/logout" />
</s:http>

<s:authentication-manager>
  <s:authentication-provider>
    <s:user-service>
        <s:user name="asif" password="123456" authorities="ROLE_USER,ROLE_ADMIN" />
        <s:user name="raheel" password="123456" authorities="ROLE_USER" />          
    </s:user-service>
  </s:authentication-provider>
</s:authentication-manager>

当我添加<s:global-method-security pre-post-annotations="enabled"/>
我的代码时显示找不到资源错误,并且当我删除我的代码时成功执行但ROLE_USER可以访问ROLE_ADMIN函数

我的控制器功能是。

@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value="/delete",method = RequestMethod.GET)
public String DeleteAll(ModelMap model,Principal principal ) {

    org.springframework.security.core.userdetails.User activeUser = (org.springframework.security.core.userdetails.User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    System.out.println("Active user is "+activeUser.getUsername()+"Authorities are "+activeUser.getAuthorities());
    return "deleteUsers";

}

Cannot construct instance of `org.springframework.security.core.authority.SimpleGrantedAuthority`

Cannot construct instance of `org.springframework.security.core.authority.SimpleGrantedAuthority`

我是用来存储oauth用户信息,通过缓存来做缓冲,结合springcache使用 

一开始在构造函数上配置@JsonCreator,后事可以反序列化,不过不知某天什么改动有报此错误。

通过编写一个自定义的反序列化器解决此问题 

class SimpleGrantedAuthorityDeserializer extends StdDeserializer<SimpleGrantedAuthority> {
    public SimpleGrantedAuthorityDeserializer() {
        super(SimpleGrantedAuthority.class);
    }
    @Override
    public SimpleGrantedAuthority deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode tree = p.getCodec().readTree(p);
        return new SimpleGrantedAuthority(tree.get("authority").textValue());
    }
}

然后在redis的反序列化使用Jackson2JsonRedisSerializer反序列化,在里面配置ObjectMapper

配置中加上

objectMapper.registerModule(new SimpleModule().addDeserializer(
        SimpleGrantedAuthority.class, new SimpleGrantedAuthorityDeserializer()));

 

最后说下jackson+redis序列化会根据get方法自动序列没有此字段的序列化字段出来,SimpleGrantedAuthority中只有role字段 ,没有authority字段,有getAuthority方法赋值role值。

"org.springframework.security.core.authority.SimpleGrantedAuthority",

{

"role": "sys_log_del",

"authority": "sys_log_del"

}

 

 

今天的关于Spring Security中角色和GrantedAuthority之间的区别spring security 角色的分享已经结束,谢谢您的关注,如果想了解更多关于25.SpringSecurity-SpringSecurityOAuth核心源码解析、33.SpringSecurity-SpringSecurity Oauth授权源码解读、@PreAuthorize批注在Spring Security中不起作用、Cannot construct instance of `org.springframework.security.core.authority.SimpleGrantedAuthority`的相关知识,请在本站进行查询。

本文标签: