使用JSR-250(@RolesAllowed)注释Spring MVC和容器认证

时间:2017-01-13 03:54:10

标签: java spring-mvc java-ee spring-security

我有一个Web应用程序,它使用Spring框架和Spring MVC来创建REST端点。容器使用Java EE安全性(特别是Active Directory支持的Wildfly 9中的安全领域)提供身份验证,并从应用程序数据库加载角色。我有一个过滤器应用的请求包装器,通过普通的Servlet API使用户详细信息可用。

的web.xml:

<web-app ... version="3.1">

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Unauthenticated Resources</web-resource-name>
            <url-pattern>/favicon.ico</url-pattern>
            <url-pattern>/NoAccess.html</url-pattern>
            <url-pattern>/login</url-pattern>
            <url-pattern>/css/*</url-pattern>
            <url-pattern>/font/*</url-pattern>
            <url-pattern>/images/*</url-pattern>
            <url-pattern>/js/*</url-pattern>
            <url-pattern>/lib/*</url-pattern>
            <url-pattern>/partials/*</url-pattern>
        </web-resource-collection>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>All Resources</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>App Realm</realm-name>
        <form-login-config>
            <form-login-page>/Login.jsp</form-login-page>
            <form-error-page>/NoAccess.html</form-error-page>
        </form-login-config>
    </login-config>

    <security-role>
        <role-name>*</role-name>
    </security-role>

</web-app>

SecurityFilter.java

@WebFilter(urlPatterns = { "*" })
public class SecurityFilter implements Filter {

    ...

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // Don't wrap unauthenticated requests
        if (httpRequest.getRemoteUser() == null) {
            chain.doFilter(httpRequest, httpResponse);
            return;
        }

        Person user = this.personRepository.findByLogin(httpRequest.getRemoteUser());
        if (user != null) {
            List<String> roles = this.personRepository.getRoles(user.getId());
            HttpServletRequest wrappedRequest = new RequestWrapper(httpRequest, user, roles);
            chain.doFilter(wrappedRequest, httpResponse);
        } else {
            httpResponse.sendRedirect(httpRequest.getContextPath() + "/NoAccess.html");
        }
    }
}

身份验证有效,并且在控制器中,我可以执行request.getUserPrincipal()request.isUserInRole("SYSTEM_ADMIN")并且它们会正确返回。现在我想为应用程序添加授权。我想在我的控制器方法上使用JSR-250 @RolesPermitted注释来应用授权规则;我不想在单独的配置文件/类中列出所有规则。控制器示例:

@RestController
@RequestMapping("/people")
public class PersonController {

    ...

    @RolesAllowed({Role.SYSTEM_ADMIN, Role.DEPARTMENT_ADMIN, Role.SYSTEM_AUDITOR, Role.AUDITOR})
    @RequestMapping
    public List<Person> listPeople(HttpServletRequest request) {
        return this.personRepository.getPeople();
    }
}

当然这本身没有任何作用,所以我添加了一个配置类@EnableWebSecurity@EnableGlobalMethodSecurity(jsr250Enabled = true)。现在,当我点击该资源时,我收到错误org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext。很公平,所以我按照https://gist.github.com/virgium03/86938e43219bc28632d0的例子,但我一直得到同样的错误。似乎过滤器永远不会被添加到过滤器链中。

我已经阅读了许多教程和SO帖子,但他们几乎都说同样的事情而且对我不起作用。必须有一些简单的我缺少 - 毕竟,所有信息都是由Spring Security做出授权决定的。这可能(可能?)我在这里缺少一些简单的东西。非常感谢所有帮助!

修改

我已将以下代码添加到我的SecurityFilter类中以填充Spring安全上下文。它有效,但我觉得这不是理想的解决方案。

List<GrantedAuthority> authorities = roles.stream()
        .map(SimpleGrantedAuthority::new)
        .collect(Collectors.toList());
PreAuthenticatedAuthenticationToken auth = new PreAuthenticatedAuthenticationToken(user, "", authorities);
auth.setDetails(new WebAuthenticationDetails(wrappedRequest));
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);

1 个答案:

答案 0 :(得分:1)

问题在于配置的身份验证提供程序。您正在使用 PreAuthenticatedAuthenticationProvider ,它会从authenticate方法返回现有的身份验证令牌。因此,如果主体为null,则忽略请求并让其他提供程序对其进行身份验证。

  

身份验证

     

public authentication authenticate(身份验证身份验证)                               throws AuthenticationException验证给定的PreAuthenticatedAuthenticationToken。如果   身份验证对象中包含的主体为null,即请求   将被忽略,以允许其他提供商对其进行身份验证。

基本上,您需要配置另一个AuthenticationProvider,它将生成Authentication令牌并在SecurityContext中设置此令牌。

相关问题