如何使用 Spring Security 保护 Vaadin 流应用程序

2024-03-16

我正在尝试将 vaadin 10 与 spring security 集成(使用 vaadin 提供的 spring 项目库),但我对它们如何准确交互感到困惑。如果我转到受保护的网址(在本例中为“/about”),直接在浏览器中键入它,则会显示登录页面。如果我通过单击 UI 中的链接访问相同的 URL,即使我未经过身份验证,该页面也会显示。所以我猜 Vaadin 不会通过 Spring Security 的过滤器链,但是我如何保护 UI 内的资源,以及如何在 vaadin 和 spring 之间共享经过身份验证的用户?我应该实施两次安全措施吗?可用的文档似乎没有涵盖这一点,互联网上的每个链接都有 Vaadin 7-8 的示例,我从未使用过它,并且其工作方式似乎与 10+ 不同。

有谁知道这方面的任何资源,或者您能否启发我所有这些如何协同工作,以便我知道我在做什么?

这是我的安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String[] ALLOWED_GET_URLS = {
        "/",
        //"/about",
        "/login/**",
        "/frontend/**",
        "/VAADIN/**",
        "/favicon.ico"
    };

    private static final String[] ALLOWED_POST_URLS = {
        "/"
    };

    //@formatter:off
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .authorizeRequests()
                .mvcMatchers(HttpMethod.GET, ALLOWED_GET_URLS)
                    .permitAll()
                .mvcMatchers(HttpMethod.POST, ALLOWED_POST_URLS)
                    .permitAll()
                .anyRequest()
                    .fullyAuthenticated()
             .and()
                .formLogin()
                    .loginPage("/login")
                    .permitAll()
            .and()
                .logout()
                    .logoutSuccessUrl("/")
                    .permitAll();
    }
    //@formatter:on

}

使用 Vaadin Flow (12.0.2)、Spring Boot Starter (2.0.2.RELEASE) 和 Spring Boot Security,基本上,我发现使用以下方式基于角色/权限进行授权;

基于路由/上下文的角色/权限管理

  • Spring安全(HttpSecurity)
  • Vaadin API(BeforeEnterListener 和路线/导航 API)

业务部门角色/权限管理

  • 在代码内部使用 HttpServletRequest.isUserInRole 方法

让我们从一个简单的 Spring Security 配置示例开始;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig
        extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable() // CSRF is handled by Vaadin: https://vaadin.com/framework/security
                .exceptionHandling().accessDeniedPage("/accessDenied")
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
                .and().logout().logoutSuccessUrl("/")
                .and()
                .authorizeRequests()
                // allow Vaadin URLs and the login URL without authentication
                .regexMatchers("/frontend/.*", "/VAADIN/.*", "/login.*", "/accessDenied").permitAll()
                .regexMatchers(HttpMethod.POST, "/\\?v-r=.*").permitAll()
                // deny any other URL until authenticated
                .antMatchers("/**").fullyAuthenticated()
            /*
             Note that anonymous authentication is enabled by default, therefore;
             SecurityContextHolder.getContext().getAuthentication().isAuthenticated() always will return true.
             Look at LoginView.beforeEnter method.
             more info: https://docs.spring.io/spring-security/site/docs/4.0.x/reference/html/anonymous.html
             */
        ;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password("$2a$10$obstjyWMAVfsNoKisfyCjO/DNfO9OoMOKNt5a6GRlVS7XNUzYuUbO").roles("ADMIN");// user and pass: admin 
    }

    /**
    * Expose the AuthenticationManager (to be used in LoginView)
    * @return
    * @throws Exception
    */
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

如您所见,我尚未在任何路由视图(用 @Route 注释)上根据角色指定任何权限。我要做的是,如果我有一个路由视图,我将在构造它(路由视图)时注册一个 BeforeEnterListener ,并在那里检查所需的角色/权限。

以下是在导航到 admin-utils 视图之前检查用户是否具有 ADMIN 角色的示例;

@Route(value = "admin-utils")
public class AdminUtilsView extends VerticalLayout { 
@Autowired
private HttpServletRequest req;
...
    AdminUtilsView() {
        ...
        UI.getCurrent().addBeforeEnterListener(new BeforeEnterListener() {
            @Override
            public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
                if (beforeEnterEvent.getNavigationTarget() != DeniedAccessView.class && // This is to avoid a
                        // loop if DeniedAccessView is the target
                        !req.isUserInRole("ADMIN")) {
                    beforeEnterEvent.rerouteTo(DeniedAccessView.class);
                }
            }
        });
    }
}

如果用户没有 ADMIN 角色,他将被路由到 DeniedAccessView,这在 Spring Security 配置中已被允许。

@Route(value = "accessDenied")
public class DeniedAccessView
        extends VerticalLayout {
    DeniedAccessView() {
        FormLayout formLayout = new FormLayout();
        formLayout.add(new Label("Access denied!"));
        add(formLayout);
    }
}

在上面的示例 (AdminUtilsView ) 中,您还可以通过自动装配 HttpServletRequest 在 Vaadin 代码中看到 HttpServletRequest.isUserInRole() 的用例。

SUMMARY:如果你的视图有Route,请先使用BeforeEnterListener对请求进行授权,否则使用Spring Security 用于休息服务等的匹配器(例如 regexMatchers 或 antMatchers)。

NOTE:对于同一个规则同时使用 Vaadin 路由和 Spring Security 匹配器规则可能有点扭曲,我不建议这样做(它会导致 Vaadin 中的一些内部循环;例如,假设我们有一个使用 /view 路由的视图和一个条目Spring Security for /view 具有所需的角色。如果用户缺少此类角色并且他被路由/导航到此类页面(使用 Vaadin 路由 API),Vaadin 会尝试打开与该路由关联的视图,而 Spring security 会避免由于缺少角色)。

另外,我认为,在重新路由或将用户导航到不同的视图/上下文之前,使用 Vaadin 流导航 API 的一个好习惯是检查所需的角色/权限。

此外,为了在 Vaadin 中使用 AuthenticationManager 的示例,我们可以有一个基于 Vaadin 的 LoginView ,类似于;

@Route(value = "login")
public class LoginView
        extends FlexLayout implements BeforeEnterObserver {

    private final Label label;
    private final TextField userNameTextField;
    private final PasswordField passwordField;

    /**
    * AuthenticationManager is already exposed in WebSecurityConfig
    */
    @Autowired
    private AuthenticationManager authManager;

    @Autowired
    private HttpServletRequest req;

    LoginView() {
        label = new Label("Please login...");

        userNameTextField = new TextField();
        userNameTextField.setPlaceholder("Username");
        UiUtils.makeFirstInputTextAutoFocus(Collections.singletonList(userNameTextField));

        passwordField = new PasswordField();
        passwordField.setPlaceholder("Password");
        passwordField.addKeyDownListener(Key.ENTER, (ComponentEventListener<KeyDownEvent>) keyDownEvent -> authenticateAndNavigate());

        Button submitButton = new Button("Login");
        submitButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
            authenticateAndNavigate();
        });

        FormLayout formLayout = new FormLayout();
        formLayout.add(label, userNameTextField, passwordField, submitButton);
        add(formLayout);

        // center the form
        setAlignItems(Alignment.CENTER);
        this.getElement().getStyle().set("height", "100%");
        this.getElement().getStyle().set("justify-content", "center");
    }

    private void authenticateAndNavigate() {
        /*
        Set an authenticated user in Spring Security and Spring MVC
        spring-security
        */
        UsernamePasswordAuthenticationToken authReq
                = new UsernamePasswordAuthenticationToken(userNameTextField.getValue(), passwordField.getValue());
        try {
            // Set authentication
            Authentication auth = authManager.authenticate(authReq);
            SecurityContext sc = SecurityContextHolder.getContext();
            sc.setAuthentication(auth);

            /*
            Navigate to the requested page:
            This is to redirect a user back to the originally requested URL – after they log in as we are not using
            Spring's AuthenticationSuccessHandler.
            */
            HttpSession session = req.getSession(false);
            DefaultSavedRequest savedRequest = (DefaultSavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
            String requestedURI = savedRequest != null ? savedRequest.getRequestURI() : Application.APP_URL;

            this.getUI().ifPresent(ui -> ui.navigate(StringUtils.removeStart(requestedURI, "/")));
        } catch (BadCredentialsException e) {
            label.setText("Invalid username or password. Please try again.");
        }
    }

    /**
    * This is to redirect user to the main URL context if (s)he has already logged in and tries to open /login
    *
    * @param beforeEnterEvent
    */
    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        //Anonymous Authentication is enabled in our Spring Security conf
        if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken)) {
            //https://vaadin.com/docs/flow/routing/tutorial-routing-lifecycle.html
            beforeEnterEvent.rerouteTo("");
        }
    }
}

最后,这是可以从菜单或按钮调用的注销方法:

/**
 * log out the current user using Spring security and Vaadin session management
 */
void requestLogout() {
    //https://stackoverflow.com/a/5727444/1572286
    SecurityContextHolder.clearContext();
    req.getSession(false).invalidate();

    // And this is similar to how logout is handled in Vaadin 8:
    // https://vaadin.com/docs/v8/framework/articles/HandlingLogout.html
    UI.getCurrent().getSession().close();
    UI.getCurrent().getPage().reload();// to redirect user to the login page
}

您可以继续使用 Spring UserDetailsS​​ervice 完成角色管理并通过查看以下示例创建一个 PasswordEncoder bean:

  • https://github.com/igor-baiborodine/vaadin-demo-bakery-app/blob/master/src/main/java/com/kiroule/vaadin/bakeryapp/app/security/SecurityConfiguration.java https://github.com/igor-baiborodine/vaadin-demo-bakery-app/blob/master/src/main/java/com/kiroule/vaadin/bakeryapp/app/security/SecurityConfiguration.java
  • https://github.com/igor-baiborodine/vaadin-demo-bakery-app/blob/master/src/main/java/com/kiroule/vaadin/bakeryapp/app/security/UserDetailsS​​erviceImpl.java https://github.com/igor-baiborodine/vaadin-demo-bakery-app/blob/master/src/main/java/com/kiroule/vaadin/bakeryapp/app/security/UserDetailsServiceImpl.java
  • https://www.baeldung.com/role-and-privilege-for-spring-security-registration https://www.baeldung.com/role-and-privilege-for-spring-security-registration
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Spring Security 保护 Vaadin 流应用程序 的相关文章

  • RSA 加密-解密:BadPaddingException:数据必须以零开头

    对于一个被问了很多次的问题 我很抱歉向您询问您的技能 我有一个关于 RSA 加密的问题 我已经检查过有关此问题的其他主题 但没有找到任何有用的答案 我希望你能帮助我 我想读取一个文件 加密其内容 然后解密它并将这些解密的字节放入一个新文件中
  • Selenium - 保存网站,包括所有图像、css、dom

    我想使用 firefox 或 chrome 访问带有 selenium 的页面 当页面加载时 我想从页面下载所有图像 css dom 我想存储每张图像 就像我在其中找到它们一样 chrome gt Tools gt Development
  • @NotNull.List 的目的

    当我查看标准时限制条件 http docs oracle com javaee 6 api javax validation constraints package summary html在 Bean Validation API JSR
  • 将二进制数据的 byte[] 转换为 String

    我有二进制格式的数据 hex 80 3b c8 87 0a 89 我需要将其转换为字符串 以便通过 Jackcess 将二进制数据保存在 MS Access 数据库中 我知道 我不打算在 Java 中使用 String 来存储二进制数据 但
  • Java 将字节转换为二进制安全字符串

    我有一些以字节为单位的数据 我想将它们放入Redis中 但是Redis只接受二进制安全字符串 而我的数据有一些二进制非安全字节 那么如何将这些字节转换为二进制安全字符串以便将它们保存到 Redis 中呢 Base64 对我有用 但它使数据更
  • Java - 同步方法导致程序大幅减慢

    我正在尝试了解线程和同步 我做了这个测试程序 public class Test static List
  • 从 org.w3c.dom.Node 获取 Xpath

    我可以从 org w3c dom Node 获取完整的 xpath 吗 假设当前节点指向 xml 文档中间的某个位置 我想提取该元素的 xpath 我正在寻找的输出 xpath 是 parent child1 chiild2 child3
  • 如何将 wsdl 内部架构设置为 Jaxb2Marshaller 以验证我所做的每篇文章?

    我正在使用 SOAP Web 服务 在调用它之前我必须验证每个 xml 帖子 所以我正在使用 The CXF codegen 插件生成POJO树结构 第三部分 wsdl xxxx soap service wsdl 一个类实现Web服务网关
  • Keycloak 社交登录 REST API

    我已经为我的 keycloak 实例启用了谷歌社交登录 但我需要将其用作休息服务 是否有可用于执行此操作的端点 Keycloak 中没有 Google 身份验证 API 但您可以使用以下方法解决它代币交换 https www keycloa
  • 为什么 Java 中的 hashCode() 可以对不同对象返回相同的值?

    引用我正在读的书中的一段话首先Java http www amazon co uk Head First Java Kathy Sierra dp 0596009208 关键是 哈希码可以相同 但不一定保证对象相等 因为使用的 哈希算法 h
  • JSP 作为电子邮件模板

    有没有办法发送 MIME 电子邮件 其中电子邮件正文源自 JSP 我需要使用 Javamail 发送一封电子邮件 其中包含一个表格 我认为如果我可以使用 JSP 来完成所有格式设置和布局 将会很方便 在这个线程中 Java 电子邮件模板的建
  • 日志记录在 Android 设备上实际上有什么作用?

    我一直在 Android 示例中看到这样的代码 try catch Exception e Log e Error e getMessage 什么是Log e实际上在物理设备上做什么 它进入系统日志 开发人员可以通过 SDK 工具访问该日志
  • 获取证书链

    我正在 Java 中使用 X509 证书 给定一个证书 是否可以在签名层次结构中找到所有其他证书 直到找到根证书 我有一个证书文件 带有 cer扩展名 我想提取父签名证书 我想继续查找该证书的父证书 直到获得最终的自签名根证书 我已经检查了
  • 在openjdk:7-jre-alpine docker上如何安装python 3.6

    直到大约一周前 我才在 java 图像上成功使用 python 3 6 脚本 如下所示 FROM openjdk 7 jre alpine RUN apk update apk upgrade apk add no cache bash a
  • 如何在Webview中保存用户名和密码

    目前 我还在学习Android开发的过程中 所以如果我的这个问题对你来说不太容易理解 请原谅 我创建了一个 Android 应用程序 它使用 RecyclerView 显示一组列表 当用户单击列表中的每个名称时 它会将它们重定向到一组不同的
  • HashSet 与 LinkedHashSet

    它们之间有什么区别 我知道 LinkedHashSet 是 HashSet 的有序版本 维护一个跨所有元素的双向链接列表 使用此类代替 HashSet 当您关心迭代顺序时 当你迭代 HashSet 时 顺序是不可预测的 而 LinkedHa
  • Spring Boot如何加入自定义查询

    我需要创建一个端点 该端点按州返回人口普查数据以及城市列表 我目前使用两个端点来获取此数据 目前回应 自定义查询一 censusByState id 1 code 11 name Rond nia statePopulation 18152
  • 如何将多部分文件从另一个服务发送到一个服务

    我有两个端点 api 它们是 uploadand 重定向 upload是我直接上传文件的地方 重定向是我接收文件并将其传递给上传并获取 JSON 响应的地方 upload 所以下面是我的代码 package com example impo
  • Android应用程序中的模式输入

    我想知道是否有其他替代方案可以替代 Android 上平庸的 EditText 密码输入 是否有 API 或开源代码可以集成到我的应用程序中 类似于锁屏图案解锁 Intent 可能会返回哈希值 数字 字符串或代表用户输入的模式的任何内容 我
  • 如何正确使用Google Calendar API Events.Insert命令?

    所以我一直使用REST方法来调用Google的API 我需要将事件插入到我拥有 ID 的特定日历中 这是我发送的 POST 请求 地址 https www googleapis com calendar v3 calendars https

随机推荐

  • Caffe 准确率大于 100%

    我正在构建一个 但是当我使用上提供的自定义火车功能时莱内特示例 http nbviewer ipython org github BVLC caffe blob master examples 01 learning lenet ipynb
  • 如何使用客户端/服务器 Nailgun(在 Debian Stretch 上)运行简单的 Java 程序?

    我尝试使用Nailgun http www martiansoftware com nailgun on Debian GNU Linux 9 延伸 Nailgun 是一个客户端 协议和服务器 用于从命令行运行 Java 程序 而不会产生
  • 恢复用python删除的文件

    所以 我使用 python 删除了一个文件 我在回收箱中找不到它 有什么办法我可以undo它或其他东西 提前致谢 编辑 我用过os remove 我尝试过Recuva 但似乎没有发现任何东西 我做了深入的搜索 如果你用过os remove并
  • 使用 4 个线程获取/释放语义

    我目前正在阅读 Anthony Williams 撰写的 C Concurrency in Action 他的清单之一显示了这段代码 他指出以下断言z 0可以开火 include
  • 我应该使用 java.text.MessageFormat 来处理没有占位符的本地化消息吗?

    我们正在本地化在 Java 5 上运行的 Web 应用程序的用户界面文本 并且对于如何输出在属性文件中定义的消息 使用的类型 遇到了困境 java util Properties http java sun com j2se 1 5 0 d
  • 宏有什么好的用途吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 据我所知 宏在编译器正确看到程序文本之前就重新排列了它 因此可能会导致问题 我很少在 C 代码中看到它们 大部分是在 C 代码中 我所
  • SwiftUI 工作表显示包含错误数据的工作表

    我有一个显示 1 2 和 3 的列表 点击文本时 应用程序会打开一张包含点击数字的工作表 但是 如果我点击第二行或第三行中的文本 工作表中显示的数字仍然是 1 我做错了什么 import SwiftUI struct ContentView
  • Mac OS 登录项是否带有参数?

    在 Mac OS 中 我创建了一些 AppleScript 来添加和删除启动应用程序 帐户 系统窗格下的 登录项 bin bash usr bin osascript e tell application System Events to
  • 在最后一次出现的字符处拆分然后连接

    我想分割一个attribute在最后一次出现字符时 添加一个字符串并将数组重新连接在一起 这是一个简化的demo https jsfiddle net Ldjoqtk1 1 在演示中我想拆分src最后一次出现的属性 然后添加 fx to t
  • Node.js 生成的 csv 文件显示英镑符号 (£)

    我正在用这个json2scv https github com zemirco json2csv包解析我的数据 示例 json 数据在下面的代码中描述 我正在尝试使用以下代码在我的 Node js 应用程序中生成 CSV 文件 如果我在 E
  • jquery 对话框打开时窗口向上滚动

    我正在尝试使用 jquery 1 4 和 jquery ui 1 8rc3 custom js 打开模态 jquery 对话框 在所有浏览器中 对话框打开都没有问题 但在 IE 7 和 6 中 对话框打开后 窗口会自行滚动到底部 我尝试将窗
  • 如何将 Selenium 输出导出到 PHP?

    Firefox 的 Selenium IDE 似乎没有默认导出到 PHP 我需要 Selenium 才能将其输出转换为 PHP 以在 Drupal 中使用 如何安装PHP语言导出才能达到这个目标 现在它是 mozilla 中的一个附加组件
  • 显示 GDB 中当前的汇编指令

    我正在 GDB 中进行一些汇编级调试 有没有办法让 GDB 以与显示当前源代码行相同的方式显示当前的汇编指令 每个命令后的默认输出如下所示 0x0001433f 990 Foo bar p 这给了我当前指令的地址 但我必须继续参考disas
  • Bokeh HTML 模板格式化程序不起作用

    请看一下页面 https docs bokeh org en latest docs reference models widgets tables html bokeh models widgets tables HTMLTemplate
  • Express 路由器参数验证

    Express 4x api 文档声称您可以将正则表达式作为第二个参数传递给router param http expressjs com 4x api html router param为了验证参数 该方法现在可用于有效验证参数 以及 可
  • Unity如何在玩家移动时让我的枪停止射击?

    I am making a gun shooting script but I do not know how to disable shooting when the player is running Can someone help
  • fancyBox2:幻灯片按钮和图像边框

    是否可以只有幻灯片开 关按钮和位置 标题区域内的某个地方 我想要这个的原因是 因为按钮栏缩小了显示图像的大小 我只想有这个选项 打开或关闭幻灯片 导航通过正常的左右方向完成 单击图像 是否可以减少图像周围的边框 尤其是底部边框 我尝试了填充
  • 通过 TeamViewer 连接时全屏 WPF 应用程序崩溃

    我创建了一个运行相当稳定的 WPF 应用程序 但有一个问题我无法解决 当我通过 TeamViewer 版本 11 连接到正在运行 WPF 应用程序 全屏模式 的平板电脑时 应用程序崩溃 当我结束远程连接时也会发生同样的情况 我得到以下异常
  • Ubuntu 14.02 上的 Tkinter 将宽度报告为两个显示器的总和

    我在新的 Ubuntu 14 02 机器上有两个显示器 一台显示器为 1024X768 VGA 第二台显示器是 1920X1080 HDMI 无论我做什么 root winfo screenwidth 报告 2944 两个显示器的总和 如果
  • 如何使用 Spring Security 保护 Vaadin 流应用程序

    我正在尝试将 vaadin 10 与 spring security 集成 使用 vaadin 提供的 spring 项目库 但我对它们如何准确交互感到困惑 如果我转到受保护的网址 在本例中为 about 直接在浏览器中键入它 则会显示登录