这一节我们要展示将Spring Security、Spring Boot和Angular JS放在一起使用的一些不错的特性,它们能提供令人愉快且安全的用户体验。对于Spring和Angular JS的初学者而言易于上手,而且也能为专家们所用。这实际上是有关于Spring Security和Angular JS的一系列文章的首篇,成功地对新特性进行了逐一的阐述。我们将会在第二阶段以及后续阶段中对应用程序进行提升,不过伺此后的主要变化是架构上的,而不是功能方面的。
HTML5,丰富的基于浏览器的特性,以及“单页面应用程序”对于现代开发者而言都是极具价值的工具,但任何具备实际意义的交互都将涉及到后台服务器,静态内容(HTML, CSS 和 JavaScript)也是如此,因而我们将需要一个后台服务器。后台服务器可以扮演大量的角色:托管静态内容,有时(不过如今不是那么频繁了)渲染动态的HTML,对用户进行认证,为需要受到保护的资源提供安全访问机制,还有(最后但并不代表最不重要的一个)就是在浏览器中通过HTTP和JSON(有时也被称作REST API)同JavaScript进行交互。
Spring 一向是用来构建后台功能(特别是在企业级领域中)的流行技术,而随着 Spring Boot 这样的事物的出现,这方面的工作变得从未如此简单。让我们来看看如何使用Spring Boot、Angular JS以及Twitter 的Bootstrap 从零开始去构建一个新的单页面应用程序。选择这一技术栈并没有特殊原因,不过是因为相当流行,尤其是核心Spring在企业级Java市场深受青睐, 因此值得将其作为起点。
我们准备逐步完成这个应用程序的创建过程,并进行一些详细描述,因此那些并不完全了解 Spring 和 Angular 的人也可以跟着一起搞清楚。如果你更愿意省掉这个环节,可以 跳到最后 ,应用程序可以运作起来了,来看看所有的东西是如何契合到一起的。创建一个新工程有多种不同的选择:
完整的工程源代码在 Github 上, 因此你可以克隆该工程,然后直接从你喜欢的地方开始。然后跳到下一节。
创建新工程的最简单方式就是通过 Spring Boot Initializr. 例如,在类Unix系统上使用curl:
$ mkdir ui && cd ui $ curl https://start.spring.io/starter.tgz -d style=web \ -d style=security -d name=ui | tar -xzvf -
然后你就可以将该工程 (默认是一个普通的Maven Java工程)导入你喜欢的IDE,或者直接操作文件和命令行上的“mvn”。然后跳到 下一节。
你可以使用 Spring Boot CLI 创建相同的工程,像这样:
$ spring init --dependencies web,security ui/ && cd ui
然后跳转到 下一节。
愿意的话你一样也可以从 Spring Boot Initializr 网站直接下载zip格式的代码。只要在你的浏览器中打开它并选择依赖 "Web" 和 "Security", 然后在 "Generate Project" 点击就行了。zip文件的跟目录中包含了一个标准的 Maven 或者 Gradle 工程, 所以你可以在解压之前先创建一个空目录。然后跳转到下一节。
在 Spring Tool Suite ( 一个 Eclipse 插件的集合) 中,你也能够利用一个位于 File->New->Spring Starter Project 的对话框创建和导入工程。然后跳转到下一节。
单页面应用程序的核心就是一个静态的"index.html", 所以我们首先来创建一个 (在 "src/main/resources/static" 或者 "src/main/resources/public" 中):
index.html
<!doctype html> <html> <head> <title>Hello AngularJS</title> <link href="css/angular-bootstrap.css" rel="stylesheet"> <style type="text/css"> [ng\:cloak], [ng-cloak], .ng-cloak { display: none !important; } </style> </head> <body ng-app="hello"> <div class="container"> <h1>Greeting</h1> <div ng-controller="home" ng-cloak class="ng-cloak"> <p>The ID is {{greeting.id}}</p> <p>The content is {{greeting.content}}</p> </div> </div> <script src="js/angular-bootstrap.js" type="text/javascript"></script> <script src="js/hello.js"></script> </body> </html>
这个文件简短得可爱,因为它只是说了句 "Hello World"。
重要的特性包括:
一些在<head>中引入的CSS,为一个还不存在的文件放置的占位符, 不过进行了富有含义的命名("angular-bootstrap.css") 还有一个定义在"ng-cloak"类中的内联样式表。
"ng-cloak" 类被应用于内容<div>,如此内容就可以一直隐藏知道Angular JS有机会对其进行处理 (这就在初始化页面加载期间的"闪烁")。
<body> 被标记为 ng-app="hello" ,其意义就是我们需要定义一个JavaScript模块,Angular就会将其识别为一个叫做"hello"的应用程序。
所有的CSS类(除开"ng-cloak"之外的)都来自Twitter Bootstrap。只要我们正确设置好样式表,它们就能让东西变得美观起来。
问候语的内容是使用双中括弧对来标识的, 例如 {{greeting.content}},这样稍后就会有Angular (根据外围的<div>上的ng-controller指令,使用的是一个叫做“home" 的 "控制器")对其进行过滤。
Angular JS (以及Twitter Bootstrap) 是在<body>的底部引入的,那样浏览器就可以在处理之前先处理所有的HTML。
我们还引入了一个单独的 "hello.js",这里是我们定义应用程序行为的地方.
我们准备在稍后创建脚本和样式表资源,但现在我们可以忽视掉它们还不存在的事实。
等你添加好了主页文件,应用程序就可以在一个浏览器中被加载了(竟然它还并不能做太多事情)。你可以在命令行上面这样做:
$ mvn spring-boot:run
然后在浏览器中访问 http://localhost:8080。 当你加载主页时,应该会遇到一个浏览器对话框,要求输入用户名和密码 (用户名就是 "user",而密码则会在启动时打印在控制台日志中)。因为实际上还没有任何内容,因此在成功的通过了认证之后,你应该会得到一个空白的页面,只会显示一个"Greeting"(问候语)标题。
如果你不喜欢在控制台日志里寻找密码,只要在 "application.properties" ("src/main/resources"目录下)添加上这个就行了: security.user.password=password (并且选择使用你自己的密码)。我们在示例代码中利用 "application.yml" 这样做过。
在IDE中,只要在应用程序类中运行main()方法(只有一个类,如果你使用的是上述的“crul”命令,它就是被叫做 UiApplication)就行了。
要打包和运行一个单独的JAR包的话,你可以这样做:
$ mvn package $ java -jar target/*.jar
Angular和其它前端技术的入门级教程经常从互联网直接引用前端库的资源(例如. Angular JS 网站 自己就推荐从 Google CDN 下载资源)。我们不会这样做,而是会通过将几个来自于这种库的文件结合起来,生成 "angular-bootstrap.js" 这个资源。要使得应用程序运行起来,这样做并不是严格必要的,不过这样做对于生产环境上的应用程序而言,通过整合脚本来避免浏览器和服务器间(或者内容分发网络上)的过于频繁的访问,不失为一个最佳实践。因为我们不要对CSS样式表进行修改或者定制,因此也没必要生成 "angular-bootstrap.css" 了, 而我们也可以就只利用来自资源Google CDN的静态资源就行了。不过,在实际的应用程序我我们几乎肯定会希望去修改样式表,并且我们应该不会想手工地区修改CSS资源,因此我们会使用一种更高级的工具(例如. Less 或者 Sass), 因此我们也准备要使用一个。
有很多方法可以实现目的,但就此节的目的而言,我们将使用 wro4j, 它是一个基于Java的技术链,用来对前端资源进行预处理和打包。可以将其作为一个JIT(即时)过滤器(Filter)用在任何Servlet应用程序中, 而它也能很好的支持像Maven和Eclipse这样的构建工具,我们也正是要这样使用它。于是我们将要去构建静态资源文件,并将它们打包到应用程序的JAR包中。
旁白: 对于首席(hard-core)前端开发人员而言,Wro4j 可能不是其选择 - 他们应该会使用基于node的工具链, bower 和/或者 grunt 。这些也是相当优秀的工具,而且在互联网上的资料也很全面, 所以如果你想用的话尽管用就是了。如果你只是将这些工具的输出放到 "src/main/resources/static"中,那么完全会起作用。我觉得wro4j方便是因为我并非首席前端开发人员,并且我也知道如何使用基于Java的工具。
为了在编译时创建静态资源,我们往Maven的pom.xml添加了一些魔法 (它相当的冗长,但是是样板化的,因此在Maven中它可以被提炼到一个父pom中去, 或者是在Gradle中的一个共享任务或者插件):
pom.xml
<build> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> </resource> <resource> <directory>${project.build.directory}/generated-resources</directory> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <!-- Serves *only* to filter the wro.xml so it can get an absolute path for the project --> <id>copy-resources</id> <phase>validate</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${basedir}/target/wro</outputDirectory> <resources> <resource> <directory>src/main/wro</directory> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>ro.isdc.wro4j</groupId> <artifactId>wro4j-maven-plugin</artifactId> <version>1.7.6</version> <executions> <execution> <phase>generate-resources</phase> <goals> <goal>run</goal> </goals> </execution> </executions> <configuration> <wroManagerFactory>ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory</wroManagerFactory> <cssDestinationFolder>${project.build.directory}/generated-resources/static/css</cssDestinationFolder> <jsDestinationFolder>${project.build.directory}/generated-resources/static/js</jsDestinationFolder> <wroFile>${project.build.directory}/wro/wro.xml</wroFile> <extraConfigFile>${basedir}/src/main/wro/wro.properties</extraConfigFile> <contextFolder>${basedir}/src/main/wro</contextFolder> </configuration> <dependencies> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>angularjs</artifactId> <version>1.3.8</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.2.0</version> </dependency> </dependencies> </plugin> </plugins> </build>
你可以逐行复制到你的POM中去,或者如果对 Github中的资源 正一直关注的话,可以就只上扫上一眼。要点是:
我们引入了一些网页jar包作为依赖(jquery 和 bootstrap 用于CSS和样式, 还有Angular JS 用于业务逻辑)。在这些jar文件中的一些静态资源将会被引入到我们生成的"angular-bootstrap.*" 文件中,而jar文件本身并不需要同应用程序一起打包。
Twitter Bootstrap 对 jQuery有依赖,因此我们也对其进行了引用。一个没有使用到Bootstrap的AngularJS应用程序不会需要,因为需要来自于jQuery的功能特性,Angular尤其自己的版本。
生成的资源会放到 "target/generated-resources" 中, 而且因为被声明到了 <resources/> 中,它们会被打包到项目的输出的JAR包, 并且在IDE中的类路径(classpath)中可用(只要我们使用了Maven相关的工具,例如Eclipse的m2e)。
wro4j-maven-plugin 拥有一些集成到Eclipse的功能,可以从Eclipse Marketplace出对其进行安装 (如果这是第一次遇到,那稍后再去尝试——它对于完成这个应用程序并不是必须的)。如果你安装了的话,那么Eclipse会盯着这些资源文件,并且如果它们发生了变化,那么就会重新生成输出。如果你是以调试模式运行的,那么改变就可以在浏览器中立即重新载入。
Wro4j 是受制于一个XML配置文件的,这个配置文件并不知道你的构建类路径,只知道绝对的文件路径,因此我们得创建一个绝对文件路径并将其插入wro.xml中。为此我们会使用Maven资源过滤,而这就是为什么会有一个明确的 "maven-resources-plugin" 声明。
这就是我们需要对POM进行的全部修改。它仍然要添加wro4j构建文件,这个我们所指定的文件将位于 "src/main/wro" 中。
如果你看看Github上的源代码,你只会看到3个文件 (而且其中一个还是空着的,准备后续的定制):
wro.properties 是一个面向wro4j中预处理和渲染引擎的配置文件。你可以用它来在工具链的不同部分进行切换。在这种情况下我们用它来从 Less 编译出CSS,并且对 JavaScript 进行压缩, 最终将我们需要的所有的库的源文件结合到两个文件中。
wro.properties
preProcessors=lessCssImport postProcessors=less4j,jsMin
wro.xml 声明了单独“一组”叫做 "angular-bootstrap" 的资源, 而这最终就是我们所生成的静态资源的基础名称。它包含了对我们所添加的web jar包中的<css>和<js>,还有一个本地资源文件filemain.less引用。
wro.xml
<groups xmlns="http://www.isdc.ro/wro"> <group name="angular-bootstrap"> <css>webjar:bootstrap/3.2.0/less/bootstrap.less</css> <css>file:${project.basedir}/src/main/wro/main.less</css> <js>webjar:jquery/2.1.1/jquery.min.js</js> <js>webjar:angularjs/1.3.8/angular.min.js</js> </group> </groups>
main.less 在示例代码中是空的, 它可以被用来定制外观,改变Twitter Bootstrap中的默认设置。例如:为了将默认颜色从蓝色改变成亮粉色,加上一行代码就行了:
main.less
@brand-primary: #de8579;
将这些文件复制到你的项目中并运行 "mvn package",然后你就会看到 "bootstrap-angular.*" 资源文件出现在你的JAR文件中。如果你现在就运行app,应该会看到CSS的效果,不过业务逻辑和导航仍然是没有的。
让我们来创建一个“hello”应用程序 (在 "src/main/resources/static/js/hello.js" 中,那可以在“index.html”底部的<script/>正确的位置找到它了。
最简单的一个AngularJS应用程序如下:
hello.js
angular.module('hello', []) .controller('home', function($scope) { $scope.greeting = {id: 'xxx', content: 'Hello World!'} })
应用程序的名称为 "hello",而它有一个空的(而且是多余的) "配置(config)", 还有一个叫做“home”的空“控制器(controller)"。"home" 控制器会在我们载入”index.html“时被调用,因为我们已经用 ng-controller="home" 对内容div进行了装饰。
注意我们将一个神奇的 $scope 注入到了控制器函数 (Angular 通过命名约定来做依赖注入, 并且会对函数参数的名称进行识别)注入了进去。$scope 随后会用于函数中去设置控制器所负责的UI元素的内容和行为。
如果你在 "src/main/resources/static/js"下面添加那个文件,你的app现在就会是安全并且实用的,它会显示 "Hello World!"。这个是有HTML中的Angular实用占位符 {{greeting.id}} 以及 {{greeting.content}} 来进行渲染的。
评论删除后,数据将无法恢复
评论(10)