- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 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 映射设置了一个映射:
一切都按其应有的方式进行。访问 http://host:port/context/api/test使用 token bbbbb
和 ccccc
时返回 200,使用 aaaaa
时返回 403。
关于java - 自定义 Spring 身份验证中的角色访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39144555/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!