Custom Spring身份验证中的角色访问

时间:2016-08-25 11:51:49

标签: java spring spring-security

我正在尝试使用Spring Security来保护我的Rest API。 所以我的要求是用户应该通过api调用在头文件中传递apiKey,并且它将被验证为w.r.t到预定义的凭证。

所以,让我们说我有apikey:' ABCdEfG ',角色:' ROLE_ADMIN '

所以我写了安全过滤器和身份验证提供程序的cutom implimentation。 关于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>

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

}

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

AuthenticationFilter

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

的AuthenticationProvider

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

}

1 个答案:

答案 0 :(得分:1)

根据您编写的安全配置

<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,这些都不受保护)必须具有ROLE_ADMIN被授予权力。

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

一旦APIKEY被验证(因此请求被验证),那么将检查CustomAuthenticationProvider返回的Authentication实例是否具有ROLE_ADMIN作为授予的autority。但是你不必自己检查它,spring-security过滤器链(org.springframework.web.filter.DelegatingFilterProxy)可以单独完成。

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

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

编辑:我非常恼火,因此我通过复制您发布的课程并构建其他内容来测试您的配置。

我像这样建立一个RetinaAuthenticationService的固定实现,就像剩下的那样,基于一个方法loadUserByApikey()的接口:

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>  

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

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

  • aaaaa&gt; ROLE_USER
  • bbbbb&gt; ROLE_ADMIN
  • ccccc&gt; ROLE_USER,ROLE_ADMIN

所有工作都应该如此。在使用bbbbb时使用令牌cccccaaaaa以及403的http://host:port/context/api/test返回200的访问权限。

相关问题