繁体   English   中英

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

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

我正在寻找一种解决方案,我可以使用 spring 引导安全性来管理用户访问,具体如下:

  1. 每个用户都绑定到一个组列表。 组是动态的,这意味着可以随时向用户分配/删除新组。

  2. 有动态创建的实体。 例如,让我们将它们视为某个博客网站上的“文章”:) 与用户类似,这些“文章”对可以访问它的组列表感到厌烦

总之:一个在运行时知道组和访问列表的系统。

我对 spring 安全性了解不多,所以我在 web 上寻找一些答案,但找不到任何东西。 所有片段都基于 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");
    }

    (...)

}

有什么方法可以创建您自己的自定义身份验证器或类似的东西,它基于(1) URL 会找到“文章”,因此允许访问的组,以及(2)用户登录,因此分配的组=>将确定我是否可以继续处理请求或立即返回 401;)

在此先感谢您的帮助!

我认为您不能仅在WebSecurityConfigurerAdapter中执行此操作,但这里有一个类似的设置,它利用了 Spring 安全性并演示了如何将访问检查添加到 controller 方法。

附加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>
    ...

(后者仅当您使用 Thymeleaf 时。)

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

在 web MVC @Controller中,任何人都可以阅读文章(无论是否登录):

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

但只有作者可以使用预览功能:

    @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) {
        ...
    }

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>

并处理登录/注销菜单以演示其他可用的安全谓词:

              <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>

(rest 是实现细节,可能有助于说明,但不一定特定于您的问题。具体来说,我建议您的逻辑适用于“动态”组。)

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

还有一个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