如果您对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 角色)
- 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之间的区别?
将GrantedAuthority视为“权限”或“权利”。这些“权限”(通常)用字符串表示(使用getAuthority()方法)。这些字符串使你可以标识权限,并让你的选民决定他们是否授予对某些内容的访问权限。
你可以通过将用户置于安全上下文中来为其授予不同的GrantedAuthority(权限)。通常,你可以通过实现自己的UserDetailsService来实现此目的,该服务返回一个UserDetails实现,该实现返回所需的GrantedAuthorities。
角色(在许多示例中使用过)只是“权限”,使用命名约定表示角色是以prefix开头的GrantedAuthority ROLE_
。没什么了 一个角色就是一个GrantedAuthority
-一个“权限”-一个“权利”。你会在Spring Security中看到很多地方,带有ROLE_
前缀的角色是专门处理的,例如在RoleVote
r中,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
UserDetailsService
将注意收集所有角色以及这些角色的所有操作,并通过返回的UserDetails实例中的方法getAuthorities(
)使它们可用。
解决方法
Spring Security中有一些概念和实现,例如GrantedAuthority
用于获得授权来授权/控制访问权限的接口。
我希望对允许的操作(例如createSubUsers
或deleteAccounts
)进行允许,这些操作将允许管理员(具有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核心源码解析
前言
- 之前我们已经完成了上面的服务提供商中:认证服务器、资源服务器的开发。 实际上代码非常简单,就是使用Spring Security OAuth的注解。 有了这些注解后我们已经可以按照标准的OAuth协议往外边发访问令牌access_token。上节演示了如何用授权码模式,密码模式获取access_token。然后拿着这些令牌去访问我们的资源服务器中的资源。
- 以上标准流程做完之后,我们接下来需要做的就是把我们之前做的三种模式(短信登录、QQ登录、微信登录)嫁接到标准模式中去。让我们在这三种模式认证通过之后。也可以返回access_token。那么为了实现这个目的,我们需要实现上面这个源码。
内容
1.Spring Security Oauth核心源码
- 上图中:绿色方块表示实体类,蓝色方块表示接口,括号中为真正实现类。
- TokenEndpoint是程序入口点,可以理解成一个controller,他会处理令牌请求。上一节中我们用授权码模式、密码模式时候,请求的url都是一致的,他是通过grant\_type来告知我们使用的是哪种请求模式。他处理/oauth/token请求。当收到令牌请求时候,TokenEndPoint会调用我们的ClientDetailsService
- ClientDetailsService区别于UserDetailsService;UserDetailsService是读取用户信息的,ClientDetailsService用来读取第三方应用信息的。我们之前发送请求时候在Header里面都会携带clientId和clientSecret来告诉是哪个应用请求授权,这个ClientDetailsService就会根据传过来的clientId和clientSecret读取响应的client配置信息。这些配置信息都会读取到ClientDetails这个对象里面去。
- ClientDetails:这里面封装的是第三方应用的信息,然后TokenEndpoint还会创建一个TokenRequest对象。
- TokenRequest:封装了我们请求的其他参数信息,比如:grant_type;如果是密码模式的话,我们的用户名、密码是什么?同时也会把ClientDetails信息放到TokenRequest里面去。因为第三方应用信息 也是令牌请求一部分。利用TokenRequest会去调用我们的TokenGranter(令牌授权者)接口,这个接口后面其实封装了我们的4种授权模式的不同实现。这个接口里面会根据你接口传上来的grant_type去挑选一个自己的实现去执行令牌的生成。不管是哪种实现,生成过程中都会产生2种东西:OAuth2Request和Authentication。
- OAuth2Request其实就是之前ClientDetails和TokenRequest信息的整合。
- Authentication接口封装了当前授权用户的一些信息。谁在做一些授权,授权用户信息就是在Authentication接口里面。它里面的信息是我们通过UserDetailsService读出来的。
- OAuth2Authentication:OAuth2Request和Authentication整合形成。它里面包含了现在是哪一个第三方应用在请求哪个用户在授权,然后用的授权模式是什么?授权参数是什么等?然后会传递给接口:AuthorizationServerTokenServices
- AuthorizationServerTokenServices拿到我们OAuth2Authentication之后,他会最终生成一个OAuth2AccessToken令牌。AuthorizationServerTokenServices的默认实现的:DefaultTokenServices里面含有两个接口的引用:TokenStore和TokenEnhancer
- TokenStore用来处理令牌存储。
- TokenEnhancer令牌生成器,当我们令牌生成之后,我们可以自定义去改造令牌。
2.源码追踪
我们使用密码模式追踪。因为授权码模式需要两步(第一步获取授权码,然后拿着授权码去获取token),测试起来比较麻烦。
然后生成TokenRequest:
//根据请求参数和客户端信息创建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里面:
里面会存在包含refresh\_token的5种模式,一次遍历找到对应目前人的授权。
然后根据传入的grantType参数,找到对应的授权类型,生成: ,然后返回回去,产生最终令牌:
密码模式的实现:
然后进入:我们使用的:ResourceOwnerPasswordTokenGranter
此时会交给 private final AuthenticationManager authenticationManager 去管理生成:Authentication 此时调用的是:ProviderManager,然后将生成的Authentication作为参数传递给:OAuth2Authentication生成一个OAuth2Authentication返回。
然后通过AbstractTokenGranter生成:OAuth2AccessToken
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授权源码解读
前言
- 我们前几节讲解Spring Security的时候,核心原理就是上图所示的过滤器链路。
- 我们核心考察的是类:FilterSecurityInterceptor;FilterSecurityInterceptor用于最后校验我们的请求是否最后能够到达我们的REST API.如果不能的话,会抛出异常,抛出异常的话,会由于其前面的ExceptionTranslationFilter处理。
- 我们这里与权限相关的主要类是:FilterSecurityInterceptor和ExceptionTranslationFilter
-
我们之前讲解的各种过滤器,现在我们主要讲解的是: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); }
-
我们之前说过,认证成功之后,我们返回的是一个: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; }
- 所以通过上面分析我们知道:如果我们认证成功,那么返回的Authentication里面的principal就是我们UserDetailsService里面封装的UserDetails信息,如果没有认证成功,那么Authentication里面的principa就是上面的"anonymousUser"字符串。
- 不管怎样,最后传给我们FilterSecurityInterceptor的Authentication都会存在,然后FilterSecurityInterceptor决定当前包含的Authentication包含的权限是否可以访问你当前请求的url。
内容
1.SpringSecurity授权逻辑图分析
我们现在主要关注:FilterSecurityInterceptor和ExceptionTranslationFilter,首先我们观察下spring security中和授权相关的类接口以及调用关系。
其中核心的类和接口就只有3个:
FilterSecurityInterceptor、AccessDecisionManager、AccessDecisionVoter。
- FilterSecurityInterceptor是我们Spring Security过滤器链路上最后的拦截器,是我们授权的主入口,FilterSecurityInterceptor其实是一个Filter
- AccessDecisionManager(访问决定管理器),其实是一个接口,他有一个抽象的实现:AbstractAccessDecisionManager和3个具体类;他是一个管理者,管理的是什么了?从名字我们可以看到,他管理的是一组:AccessDecisionVoter(授权决定投票者)
- AccessDecisionVoter会综合所有投票者的投票结果,然后给出一个最终结果过还是不过,具体判断过与不过,有3套这样的逻辑,具体的投票逻辑是在AbstractAccessDecisionManager的子类里面:
a.AffirmativeBased:只有有一个voter投通过,那么整体请求就通过。 b.ConsensusBased:比较投通过和不通过的票数, 哪一种意见多就用哪一种。
c.UnanimousBased:不管有多少个voter投通过,只要有一个投不通过。整个请求不通过。默认spring security默认使用第一种:AffirmativeBased - 那么SecurityConfig和SecurityContextHolder是干什么的呢?我们之前说要判断一个请求是否能够正常访问?需要两方面的数据:a.系统配置信息(具体url需要什么样的信息),这些配置信息我们是配置到:
FilterSecurityInterceptor会从我们的安全配置:SecurityConfig里面信息读出来,封装成一组SecurityAttribute这样的一组对象,这组对象里面其实每一个SecurityAttribute对应着一个url所对应的权限。这里面其实就是你系统配置的信息。
Authentication信息封装的基本认证信息。 - Authentication、ConfigAttribute和FilterSecurityInterceptor携带的请求信息一起传给我们的AccessDecisionManager,然后AccessDecisionManager交给 投票者,由投票者来投票是过还是不过。
2.SpringSecurity源码追踪
我们看两个流程:一个是:用户没登录时候,被拒绝访问流程,另一个是用户登录以后,访问通过流程。
2.1 用户没登录时候,被拒绝访问流程
我们访问:http://127.0.0.1:8088/
断点:FilterSecurityInterceptor
//封装请求、响应、过滤器链到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:
我们可以看到每一个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);
}
通过decide方法决定是不是让其通过.
未有授权通过就会抛出异常:异常先从:最先调用的AffirmativeBased抛出异常,然后抛出异常到父类,再次到接口,最后抛出异常到: FilterSecurityInterceptor;然后往前抛到FilterSecurityInterceptor前面的异常转换过滤器(ExceptionTranslationFilter);
然后进入: this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
把我发出去去认证。
把我发出去去认证,其实就是跳到了我们之前在:WebSecurityConfig配置里面loginPage所配置的。
2.1 用户没登录时候,被拒绝访问流程
之前是用户未登录时候的授权流程,我们现在开始登陆。
登录完成之后,我们再次访问:
http://127.0.0.1:8088/user/1
这个hasRole(''ROLE_ADMIN'')是怎么来的呢?
我们在配置文件总配置了:ADMIN
然后进入源码跟踪: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
上面我们拿到了系统配置和Authentication信息,现在我们进入投票Voter,结果如下:
最后拿到了我们的接口返回信息:
@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`
我是用来存储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`的相关知识,请在本站进行查询。
本文标签: