简体   繁体   English

具有动态匹配器和角色的 Spring 引导安全配置

[英]Spring boot security configuration with both dynamic matchers and roles

I am looking for a solution where I could use spring boot security to manage user access with specifics from below:我正在寻找一种解决方案,我可以使用 spring 引导安全性来管理用户访问,具体如下:

  1. Every user is tied to a list of groups.每个用户都绑定到一个组列表。 Groups are dynamic , meaning that a new group can be assigned/removed to/from the user at any point.组是动态的,这意味着可以随时向用户分配/删除新组。

  2. There are dynamically created entities.有动态创建的实体。 For example let's think of them as "articles" on some blog website:) Similarly like users, those "articles" are tired to a list of groups that can access it.例如,让我们将它们视为某个博客网站上的“文章”:) 与用户类似,这些“文章”对可以访问它的组列表感到厌烦

In summary : a system where groups and access list are known at runtime.总之:一个在运行时知道组和访问列表的系统。

I do not know much about spring security, so I was looking for some answers on the web, but couldn't find anything.我对 spring 安全性了解不多,所以我在 web 上寻找一些答案,但找不到任何东西。 All snippets are based on static antMatchers and roles, like this:所有片段都基于 static antMatchers 和角色,如下所示:

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/something_else_instead_of_this").hasRole("ROLE_and_instead_of_this_static_role");
    }

    (...)

}

Is there any way of creating your own custom Authenticator or something like that, which based on (1) URL would find the "article", therefore the groups allowed to access, and (2) the user logged, therefore the assigned groups => would determine if I can proceed with processing the request or return 401 immediately;)有什么方法可以创建您自己的自定义身份验证器或类似的东西,它基于(1) URL 会找到“文章”,因此允许访问的组,以及(2)用户登录,因此分配的组=>将确定我是否可以继续处理请求或立即返回 401;)

Thanks in advance for your help!在此先感谢您的帮助!

I don't think you can do this solely in the WebSecurityConfigurerAdapter but here is a similar set-up that takes advantage of Spring Security and demonstrates how to add the access checks to controller methods.我认为您不能仅在WebSecurityConfigurerAdapter中执行此操作,但这里有一个类似的设置,它利用了 Spring 安全性并演示了如何将访问检查添加到 controller 方法。

Additional pom.xml dependencies:附加pom.xml依赖项:

    ...
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    ...
    <dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
    ...

(The latter only if you're using Thymeleaf.) (后者仅当您使用 Thymeleaf 时。)

The WebSecurityConfigurerAdapter : WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@NoArgsConstructor @Log4j2
public class WebSecurityConfigurerImpl extends WebSecurityConfigurerAdapter {
    @Autowired private UserDetailsService userDetailsService;
    @Autowired private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
            .antMatchers("/css/**", "/js/**", "/images/**",
                         "/webjars/**", "/webjarsjs");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().anyRequest().permitAll()
            .and().formLogin().loginPage("/login").permitAll()
            .and().logout().permitAll();
    }
}

In the web MVC @Controller , anyone can read an article (whether logged-in or not):在 web MVC @Controller中,任何人都可以阅读文章(无论是否登录):

    @RequestMapping(method = { GET }, value = { "/article/{slug}/" })
    @PreAuthorize("permitAll()")
    public String article(Model model, @PathVariable String slug) {
        ...
    }

But only AUTHORs may use the preview feature:但只有作者可以使用预览功能:

    @RequestMapping(method = { GET }, value = { "/preview/" })
    @PreAuthorize("hasAuthority('AUTHOR')")
    public String preview(Model model) {
        ...
    }

    @RequestMapping(method = { POST }, value = { "/preview/" })
    @PreAuthorize("hasAuthority('AUTHOR')")
    public String previewPOST(Model model,
                              Principal principal, HttpSession session,
                              HttpServletRequest request,
                              @Valid PreviewForm form, BindingResult result) {
        ...
    }

This is also supported in the Thymeleaf template where the menu is conditionally displayed if the user is an AUTHOR. Thymeleaf 模板也支持这一点,如果用户是 AUTHOR,则有条件地显示菜单。

              <li th:ref="navbar-item" sec:authorize="hasAuthority('AUTHOR')">
                <button th:text="'Author'"/>
                <ul th:ref="navbar-dropdown">
                  <li><a th:text="'Preview'" th:href="@{/preview/}"/></li>
                </ul>
              </li>

And handling Login/Logout menus to demonstrate other available security predicates:并处理登录/注销菜单以演示其他可用的安全谓词:

              <li th:ref="navbar-item" sec:authorize="!isAuthenticated()">      
                <a th:text="'Login'" th:href="@{/login}"/>                      
              </li>                                                             
              <li th:ref="navbar-item" sec:authorize="isAuthenticated()">       
                <button sec:authentication="name"/>                             
                <ul th:ref="navbar-dropdown">                                   
                  <li><a th:text="'Change Password'" th:href="@{/password}"/></\
li>                                                                             
                  <li><a th:text="'Logout'" th:href="@{/logout}"/></li>         
                </ul>                                                           
              </li>

(The rest are implementation details that may be helpful for illustration but not necessarily specific to your question. Specifically, I suggest this is where your logic would be for "dynamic' groups.) (rest 是实现细节,可能有助于说明,但不一定特定于您的问题。具体来说,我建议您的逻辑适用于“动态”组。)

The UserDetailsService implementation relies on JpaRepository implementations and sets the user's grants: UserDetailsService ervice 实现依赖于JpaRepository实现并设置用户的授权:

@Service
@NoArgsConstructor @ToString @Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired private CredentialRepository credentialRepository;
    @Autowired private AuthorRepository authorRepository;
    @Autowired private SubscriberRepository subscriberRepository;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = null;
        Optional<Credential> credential =
            credentialRepository.findById(username);

        if (credential.isPresent()) {
            HashSet<GrantedAuthority> set = new HashSet<>();

            subscriberRepository.findById(username)
                .ifPresent(t -> set.add(new SimpleGrantedAuthority("SUBSCRIBER")));

            authorRepository.findById(username)
                .ifPresent(t -> set.add(new SimpleGrantedAuthority("AUTHOR")));

            user = new User(username, credential.get().getPassword(), set);
        } else {
            throw new UsernameNotFoundException(username);
        }

        return user;
    }
}

And an example of one of JpaRepository :还有一个JpaRepository的例子:

@Repository
@Transactional(readOnly = true)
public interface AuthorRepository extends JpaRepository<Author,String> {
    public Optional<Author> findBySlug(String slug);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM