gpt4 book ai didi

java - 自定义 Spring 身份验证中的角色访问

转载 作者:行者123 更新时间:2023-11-30 07:11:36 24 4
gpt4 key购买 nike

我正在尝试使用 Spring Security 来保护我的 Rest API。所以我的要求是用户应该在 api 调用的 header 中传递 apiKey,并且它将根据预定义的凭据进行验证。

假设我有 apikey:“ABCdEfG”,角色:“ROLE_ADMIN

所以我编写了安全过滤器和身份验证提供程序的自定义实现。与 apiKey 相关的身份验证工作正常,但它没有验证特定 api 所需的角色。

即如果没有 apiKey,我无法访问我的 api,但它无法验证所需的角色。

我当前的实现如下:

如果我在某个地方做错了,请告诉我。

应用程序上下文:

<security:global-method-security
pre-post-annotations="enabled" />

<security:http entry-point-ref="authenticationEntryPoint"
create-session="stateless">
<security:intercept-url pattern="/api/*"
access="ROLE_ADMIN" />
<security:custom-filter before="FORM_LOGIN_FILTER"
ref="restAuthenticationFilter" />
</security:http>

<bean id="restAuthenticationFilter"
class="com.myapp.authentication.RestAuthenticationFilter2">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
</bean>

<bean class="com.myapp.authentication.RestAuthenticationEntryPoint"
id="authenticationEntryPoint"></bean>
<bean
class="com.myapp.authentication.RestAuthenticationSuccessHandler"
id="authenticationSuccessHandler"></bean>
<bean class="com.myapp.authentication.CustomAuthenticationProvider"
id="customAuthenticationProvider"></bean>


<bean class="com.myapp.authentication.util.UserAuthenticationDAO"
factory-method="getInstance" id="userAuthenticationDAO"></bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
ref="customAuthenticationProvider" />
</security:authentication-manager>

角色.java

import org.springframework.security.core.GrantedAuthority;

@SuppressWarnings("serial")
public class Role implements GrantedAuthority {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAuthority() {
return this.name;
}

}

用户.java

import java.util.List;

import org.springframework.security.core.userdetails.UserDetails;

@SuppressWarnings("serial")
public class User implements UserDetails {

private String apiKey;

/* Spring Security related fields */
private List<Role> authorities;
private boolean accountNonExpired = true;
private boolean accountNonLocked = true;
private boolean credentialsNonExpired = true;
private boolean enabled = true;

public String getApiKey() {
return apiKey;
}

public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}

public List<Role> getAuthorities() {
return authorities;
}

public void setAuthorities(List<Role> authorities) {
this.authorities = authorities;
}

public boolean isAccountNonExpired() {
return accountNonExpired;
}

public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}

public boolean isAccountNonLocked() {
return accountNonLocked;
}

public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}

public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}

public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Override
public String getPassword() {
// TODO Auto-generated method stub
return null;
}

@Override
public String getUsername() {
// TODO Auto-generated method stub
return null;
}

@Override
public boolean equals(Object obj) {
return this.apiKey.equals(((User) obj).getApiKey());
}
}

CustomAuthentiCationToken.java

    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken {
/**
*
*/
private static final long serialVersionUID = 1L;
private String token;

public CustomAuthenticationToken(String token) {
super(null, null);
this.token = token;
}

public String getToken() {
return token;
}

@Override
public Object getCredentials() {
return null;
}

@Override
public Object getPrincipal() {
return null;
} }

身份验证过滤器

    import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import com.myapp.authentication.bean.CustomAuthenticationToken;

public class RestAuthenticationFilter2 extends AbstractAuthenticationProcessingFilter {
protected RestAuthenticationFilter2() {
super("/**");
}

@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return true;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {

String header = request.getHeader("Authorization");

if (header == null) {
throw new BadCredentialsException("No token found in request headers");
}

//String authToken = header.substring(7);
String authToken = header.trim();

CustomAuthenticationToken authRequest = new CustomAuthenticationToken(authToken);
return getAuthenticationManager().authenticate(authRequest);
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);

// As this authentication is in HTTP header, after success we need to
// continue the request normally
// and return the response as if the resource was not secured at all
chain.doFilter(request, response);
}
}

身份验证提供者

public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

@Autowired
RetinaAuthenticationService retinaAuthenticationService;

@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
// TODO Auto-generated method stub

}

@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
CustomAuthenticationToken customAuthenticationToken = (CustomAuthenticationToken) authentication;
String token = customAuthenticationToken.getToken();

User user = retinaAuthenticationService.loadUserByApiKey(token);

if (null != user) {
return user;
} else {
throw new BadCredentialsException("API token is not valid");
}
}

}

最佳答案

根据你写的安全配置

<security:http entry-point-ref="authenticationEntryPoint"
create-session="stateless">
<security:intercept-url pattern="/api/*"
access="ROLE_ADMIN" />
<security:custom-filter before="FORM_LOGIN_FILTER"
ref="restAuthenticationFilter" />
</security:http>

您声明对/api/* 的任何传入请求(这意味着 http://localhost:8080/myapp/api/test 将受到保护,但 http://localhost:8080/myapp/api/http://localhost:8080/myapp/api/more/test 都不 protected )必须具有 ROLE_ADMIN 作为授予权限。

当您将 create-session 设置为无状态时,任何请求都必须经过验证,因此您必须在每个请求中包含身份验证凭据(在本例中为 APIKEY)。

一旦 APIKEY 得到验证(因此请求得到身份验证),就会检查您的 CustomAuthenticationProvider 返回的 Authentication 实例是否具有 ROLE_ADMIN 作为授予的权限。但你不必自己检查,spring-security 过滤器链(org.springframework.web.filter.DelegatingFilterProxy)会自己检查。

因此,您无需自行访问您在 security:intercept-url 元素的 access 属性中配置的权限。

这最终意味着,如果提供者返回的 User 对象在 List 权限中具有 ROLE_ADMIN 作为权限,则将允许访问端点/api/test,否则不允许。

编辑:我很恼火,所以我通过复制您发布的类并构建其他内容来测试您的配置。

我构建了一个像这样的 RetinaAuthenticationService 的固定实现,就像剩下的部分一样,基于带有方法 loadUserByApikey() 的接口(interface):

public interface RetinaAuthenticationService {

public abstract User loadUserByApiKey(String token);

}

实现:

public class RetinaAuthenticationServiceImpl implements RetinaAuthenticationService {

private Map<String, List<String>> apiKeyRoleMappings;

@Override
public User loadUserByApiKey(String token) {
User user = null;
if(this.apiKeyRoleMappings.containsKey(token)){
user = new User();
user.setApiKey(token);
List<Role> authorities = new ArrayList<Role>();
for(String roleStr : this.apiKeyRoleMappings.get(token)){
Role role = new Role();
role.setName(roleStr);
authorities.add(role);
}
user.setAuthorities(authorities );
user.setAccountNonExpired(true);
user.setAccountNonLocked(true);
user.setCredentialsNonExpired(true);
user.setEnabled(true);
}else{
throw new BadCredentialsException("ApiKey " + token + " not found");
}
return user;
}

public Map<String, List<String>> getApiKeyRoleMappings() {
return apiKeyRoleMappings;
}

public void setApiKeyRoleMappings(Map<String, List<String>> apiKeyRoleMappings) {
this.apiKeyRoleMappings = apiKeyRoleMappings;
}


}

然后我在正在运行的项目中的 securiy-context.xml 中配置了所有内容以进行测试:

<security:http auto-config='false' pattern="/api/**" entry-point-ref="serviceAccessDeniedHandler" create-session="stateless" use-expressions="false">
<security:intercept-url pattern="/api/*" access="ROLE_ADMIN" />
<security:intercept-url pattern="/api/user/*" access="ROLE_USER,ROLE_ADMIN" />
<security:custom-filter before="FORM_LOGIN_FILTER" ref="restAuthenticationFilter" />
<security:csrf disabled="true"/>
</security:http>

<beans:bean id="restAuthenticationFilter"
class="com.eej.test.security.filter.RestAuthenticationFilter2">
<beans:property name="authenticationManager" ref="apiAuthenticationManager" />
<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
</beans:bean>

<beans:bean id="retinaAuthenticationServiceImpl" class="com.eej.test.security.services.RetinaAuthenticationServiceImpl">
<beans:property name="apiKeyRoleMappings">
<beans:map>
<beans:entry key="aaaaa">
<beans:list>
<beans:value>ROLE_USER</beans:value>
</beans:list>
</beans:entry>
<beans:entry key="bbbbb">
<beans:list>
<beans:value>ROLE_ADMIN</beans:value>
</beans:list>
</beans:entry>
<beans:entry key="ccccc">
<beans:list>
<beans:value>ROLE_USER</beans:value>
<beans:value>ROLE_ADMIN</beans:value>
</beans:list>
</beans:entry>
</beans:map>
</beans:property>
</beans:bean>

<!-- bean class="com.myapp.authentication.RestAuthenticationEntryPoint" id="authenticationEntryPoint"></bean-->
<beans:bean
class="com.eej.test.security.handler.RestAuthenticationSuccessHandler" id="authenticationSuccessHandler" />
<beans:bean class="com.eej.test.security.CustomAuthenticationProvider" id="customAuthenticationProvider" />

<!-- beans:bean class="com.myapp.authentication.util.UserAuthenticationDAO" factory-method="getInstance" id="userAuthenticationDAO" /-->
<security:authentication-manager alias="apiAuthenticationManager">
<security:authentication-provider ref="customAuthenticationProvider" />
</security:authentication-manager>

我对你的做了一些小的更改(使用预先存在的入口点引用,将一种模式应用于 security:http 部分,因为我在这个项目中已经有了一个通用的模式,将 use-expresss 设置为 false,禁用自动配置并禁用csrf),更改包名称并注释不必要的元素

我必须为我的类 RetinaAuthenticationServiceImpl 配置一个 bean,我使用此 apikey-role 映射设置了一个映射:

  • aaaaa > ROLE_USER
  • bbbbb > ROLE_ADMIN
  • ccccc > ROLE_USER、ROLE_ADMIN

一切都按其应有的方式进行。访问 http://host:port/context/api/test使用 token bbbbbccccc 时返回 200,使用 aaaaa 时返回 403。

关于java - 自定义 Spring 身份验证中的角色访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39144555/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com