如何使用Spring Boot保护单页应用程序

时间:2019-09-05 15:13:15

标签: angular spring-boot spring-security

我有一个Spring Boot应用程序,该应用程序通过keycloak(单点登录)进行身份验证,并使用Angular作为前端。

我只有1个index.html页面,这是角度应用程序的主页,我需要确保它的安全性,但是我不确定该怎么做

我遵循以下示例:https://developers.redhat.com/blog/2017/05/25/easily-secure-your-spring-boot-applications-with-keycloak/

在此示例中,他们具有不安全的index.html页面,当您单击产品链接时,它将转到spring控制器并首先针对单点登录进行身份验证,然后转发至产品页面。我验证了这一点,并看到主体被传递给控制器​​。

该演示使用的是Thimeleaf框架,因此产品页面位于模板文件夹下。

我面临2个问题

1)是我只有一个index.html页面,除非经过身份验证,否则我不希望用户完全访问它。

2)当我从角度页面触发对REST控制器的调用时,它无需身份验证即可执行调用(REST控制器中的null为空)

所以我的问题是1)如何在显示index.html之前强制其直接进行身份验证

2)为什么在调用rest服务并允许它时没有主体。

这是我的应用程序配置类

@SpringBootApplication
public class InvoiceSearchApplication {

    public static void main(String[] args) {
        SpringApplication.run(InvoiceSearchApplication.class, args);
    }


    @Configuration
    @EnableWebSecurity
    @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
    class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
    {
        /**
         * Registers the KeycloakAuthenticationProvider with the authentication manager.
         */
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
            keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
            auth.authenticationProvider(keycloakAuthenticationProvider);
        }

        /**
         * Defines the session authentication strategy.
         */
        @Bean
        @Override
        protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
            return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
        }

        @Bean
        public KeycloakConfigResolver KeycloakConfigResolver() {return new KeycloakSpringBootConfigResolver();}

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            super.configure(http);
            http.cors().and().csrf().disable();
            http.authorizeRequests().antMatchers("/*").authenticated().anyRequest().permitAll()
             .antMatchers("/assets/*").permitAll()
             .antMatchers("/assets/img/*").permitAll() ;


        }
    }   

我将index.html放在src / main / resources / static下 然后将所有css和js文件放在src / main / resources / static / assets下。

这就是为什么我添加

.antMatchers(“ / assets / ”)。permitAll()                  .antMatchers(“ / assets / img / ”)。permitAll();

因此它将加载它们,否则我将在js文件中得到403禁止。

这是我的pom.xml


    <groupId>com.example</groupId>
    <artifactId>product-app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>product-app</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <keycloak.version>3.1.0.Final</keycloak.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core</artifactId>
        </dependency> 

       <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
             <version>3.9</version>
        </dependency>  

        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>


    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.keycloak.bom</groupId>
                <artifactId>keycloak-adapter-bom</artifactId>
                <version>${keycloak.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

application.properties

server.context-path = /invoiceSearch
keycloak.credentials.secret=${keycloak_credentials_secret}
keycloak.realm-key=${keycloak_realm_key}
keycloak.realm=${keycloak_realm}
keycloak.auth-server-url=${keycloak_auth_server_url}
keycloak.resource=${keycloak_resource}
keycloak.ssl-required = external

我的index.html页面:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Invoice Search </title>
   <base href="/invoiceSearch/">  
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="/invoiceSearch/assets/img/mag_glass_icon.png">
<link rel="stylesheet" href="/invoiceSearch/assets/styles.eb6a472291bbfc46890c.css"></head>
<body>
  <app-root></app-root>
<script type="text/javascript" src="/invoiceSearch/assets/runtime.cf9a0e7464e091b22929.js"></script>
<script type="text/javascript" src="/invoiceSearch/assets/es2015-polyfills.bda95d5896422d031328.js" nomodule></script>
<script type="text/javascript" src="/invoiceSearch/assets/polyfills.8bbb231b43165d65d357.js"></script>
<script type="text/javascript" src="/invoiceSearch/assets/scripts.03e042f1f102bf0e2ed8.js"></script>
<script type="text/javascript" src="/invoiceSearch/assets/main.32b645cbed7ce5c0e94f.js"></script></body>
</html>

1 个答案:

答案 0 :(得分:0)

这里有很多要解压的东西。我对keycloak不太熟悉,所以我会更笼统地讲。

  1. 为防止用户访问index.html,它必须落后于spring安全性,并且必须已经进行身份验证。假设在用户获得index.html页面之前确实进行了身份验证,则需要具有spring安全设置来处理该问题,或者需要具有返回index.html页面的安全端点。这里的困难在于,对index.html的GET调用没有简单的方法来传递auth标头令牌。对于Angular应用程序,最佳解决方案是使用反向Web代理,不仅可以处理静态内容分发,还可以处理用户页面刷新,其中您需要从角度路由(mysite / ng / route1)返回index.html页面< / p>

  2. 这引起了一个问题,即如何将您的用户标记为已通过身份验证。我将假设使用令牌,因为keycloak在SSO中使用令牌。如果真是这样,则意味着您必须随每个经过身份验证的请求一起将该令牌传递回,通常是作为Bearer的auth标头,否则带有keycloak的spring应用程序将不知道您的用户是谁。除非您的index.html页上确实包含机密信息,否则我必须假定它没有,因为您的内容不安全,因此最好的解决方案是:

a。保留index.html不安全 b。在输入时,检查是否有已保存的令牌,或使用keycloak auth端点处理SSO / OAuth。您必须以某种方式让用户获得令牌。 C。将令牌保存在sessionStorage中以确保页面刷新安全。 d。将标记添加到angular的全局html标头配置中。

您还可以使用身份验证卫士根据用户身份验证状态或角色来保护角度路由,但请记住,这不是“真正的”安全性,实际上更像是一种基础。

相关问题