Spring Security 结合 Spring 存储库来保护和验证网站的各个部分

Posted

技术标签:

【中文标题】Spring Security 结合 Spring 存储库来保护和验证网站的各个部分【英文标题】:Spring Security in conjuction with Spring repositories to secure and authenticate various parts of a website 【发布时间】:2016-08-31 15:06:40 【问题描述】:

我正在寻找一个具体、严肃和完整的示例,说明如何在 Spring Boot 应用程序中使用 Spring 安全性,该应用程序使用 Spring data repositories 访问数据库并因此查询注册用户。

我已经看到,通过覆盖 configure 方法,它可以使用 Spring 安全性轻松保护一系列网页,例如使用以下选项:

http.authorizeRequests()
    .antMatchers("/", "/css/**", "/js/**", "/vendor/**", "/templates/**")
    .permitAll()
    .anyRequest()
    .authenticated()
    .and()
    .formLogin()
    .loginPage("/login")
    .permitAll()
    .and()
    .logout()
    .permitAll();

此代码可保护用户例如访问http://localhost:3000/home/users/,但允许随后访问http://localhost:3000/login 或简单地访问http://localhost:3000

我一直在阅读有关 Spring 安全性的信息,但我不知道如何保护应用程序的不同部分,例如,当用户登录网站时,并禁止他从示例中访问http://localhost:3000/home/users/another_user,通常用于控制登录用户对网站所有部分的访问。

我正在使用Spring data repositories 通过实体来操作数据库的数据。

您是否知道将 Spring 安全性与 Spring 存储库(以及必要时的其他工具)结合使用来保护(和验证)网站不同部分的示例? (视频)教程也可能有用。

感谢您的帮助。

注意:我查看了 sagan 网站的存储库,但要理解发生了什么非常复杂......

【问题讨论】:

【参考方案1】:

如上所述,ACL 是一种选择,但另一种可能更简单的解决方案可能是在方法级别应用安全性。

请参阅第 15.3 节。

https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html

所以假设你有一个 URL /users/123 ,其中 123 是当前用户,它委托给服务层方法来加载用户,那么我如何防止用户篡改 URL 并查看例如返回的数据。 /users/456.

一种方法是通过@PostAuthorize 注解应用方法级安全性:

@PostAuthorize("hasPermission(returnObject, null)")
public User findById(Long id) 
    return repository.findOne(id);

安全检查委托给 org.springframework.security.access.PermissionEvaluator 的实现

实现可能如下所示:

public class BasePermissionsEvaluator implements PermissionEvaluator 

    public boolean hasPermission(Authentication authentication, Object domainObject) 
        return hasPermission(authentication, domainObject, null);
    

    @Override
    public boolean hasPermission(Authentication authentication, Object domainObject, Object permission) 
        boolean hasPermission = true;

        //User is my custom class representing a logged in user
        //UserEntity is my custom interface implemented by entities associated with specific user
        //If user is an Admin allow access
        //Otherwise allow access if logged in user 'owns' the DomainObject instance
        User user = (User) authentication.getPrincipal();

        if(! user.isAdmin())
            if (domainObject instanceof UserEntity) 
                User owner = ((UserEntity) domainObject).getOwner();
                hasPermission = user.equals(owner);
            
        

        return hasPermission;
    

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
            Object permission) 
        return false;
    

PermissionEvaluator 的配置在 XML 中如下所示,因此您需要转换为 Java 配置:

<security:global-method-security
    pre-post-annotations="enabled">
        <security:expression-handler ref="expressionHandler"/>
</security:global-method-security>

<bean id="expressionHandler"
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>

<bean id="permissionEvaluator" class="com.mycompany.BasePermissionsEvaluator" />

以下概述了将 XML 配置转换为 Java 配置:

https://spring.io/blog/2013/07/04/spring-security-java-config-preview-method-security/#custom-method-security

因此,在您现有的安全配置类中,您应该添加:

@EnableGlobalMethodSecurity(prePostEnabled=true) //ADD THIS
public class MySecurityConfig

  @Override
  protected MethodSecurityExpressionHandler expressionHandler() 
    DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();

    //SET TO OUR CUSTOM PERMISSIONS HANDLER DETAILED ABOVE
    expressionHandler.setPermissionEvaluator(new BasePermissionsEvaluator());

    return expressionHandler;
  

【讨论】:

很抱歉很长时间没有对您的回答提供任何反馈,但我发现所有这些概念在我看来都不是微不足道和奇怪的,可能是因为我对这些东西总体上是新手,即使我已经在 Node 和 Express 中使用过 passport.js 一次。我会有第一个问题或疑问。为什么我们要将这个PostAuthorize 应用到存储库方法中?这是因为在 Spring 控制器中我们可以使用这个特定的存储库方法(包含注释)来进行一些检查吗? 似乎 Spring 安全性使所有事情都变得非常复杂,只是对于简单的情况......根据我对 passport.js 的记忆,为应用程序设置安全性要容易得多,但我可能会也错了,或者,当时,我对自己所做的事情没有完整的看法...... 我已将它放在存储库中,因为存储库正在返回评估程序所需的域对象的实例。我不能很好地在控制器上应用评估,因为这将完全返回其他东西。您可以在任何地方应用安全性,但这种特定情况会检查方法返回的域对象,因此存储库或服务层是合适的。登录的用户请求了一个实体。如果登录用户“拥有”请求的项目或具有管理员角色,则允许访问,否则拒绝访问。 好的。我会有另一个问题。到目前为止,我还没有配置文件,因为我使用的是 Spring Boot,它会为你做所有事情。我从来没有在没有 SpringBoot 的情况下使用过 Spring(但我可能也想学习它),所以我不知道将配置文件放在哪里,以及如何调用它们。官方教程好像用的是SpringBoot,而且很简单,所以很难理解到底应该怎么做。那么,我应该把这些 XML 规范放在哪里呢? 您正在使用 Java 配置,因此最好将 XML 转换为代码。这涉及向现有配置类添加注释并添加方法。确切的转换在这里详述spring.io/blog/2013/07/04/…:【参考方案2】:

这在 Spring Security 中称为访问控制,或“域对象安全”。

http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#domain-acls

你有很多阅读要做!

您可能希望将它与 Spring Data JPA 结合使用,以便 SDJ 只返回它应该返回的记录。这里有一个例子:

https://github.com/spring-projects/spring-data-examples/tree/master/jpa/security

基本上,您将向您的表添加一些“行所有者”信息,并让 SDJ 和 SS 一起工作以控制访问,例如:

@Query("select o from BusinessObject o where o.owner.emailAddress like ?#hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress")
List<BusinessObject> findBusinessObjectsForCurrentUser();

另一种方法是使用支持行安全的数据库服务器,如 PostgreSQL,并在数据库中处理您的访问控制权。

【讨论】:

以上是关于Spring Security 结合 Spring 存储库来保护和验证网站的各个部分的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 结合了 Container Security 和 JWT Token

使用 Spring Security 在一个应用程序中结合数据库和 SAML 身份验证

spring security动态管理资源结合自定义登录页面

基于RBAC的权限控制浅析(结合Spring Security)

基于RBAC的权限控制浅析(结合Spring Security)

Spring boot +Spring Security + Thymeleaf 认证失败返回错误信息