搜索词>>war 耗时0.0040
  • spring boot maven项目打war包再部署到外部tomcat中

    spring boot项目打war包再部署到外部tomcat中<h2>一.情况说明</h2> 大多数情况下我们开发的项目需要部署到外部的tomcat中,而并非spring boot默认的jar方式部署,这个时候就需要修改和新增一些配置 <h2>二.spring boot项目war打包具体操作</h2> 2.1修改pom.xml文件 <ul> <li>将<packaging>jar</packaging>修改为<packaging>war</packaging></li> <li>在<dependencies>节点中添加</li> </ul> <pre> <code class="language-xml"><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency></code></pre> <br /> 2.2创建tomcat需要的一个servlet启动类<br /> <br /> 我一般喜欢创建在spring boot启动类的同级目录<br /> <strong>ServletInitializer.java</strong> <pre> <code class="language-java">import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(CyinterfaceApplication.class); } } </code></pre> <strong><span style="color:#ff0000">Tips</span>:CyinterfaceApplication即spring boot默认的启动类</strong> <h2>三.使用maven打包项目</h2> 由于我直接使用的eclipse集成的maven插件,使用方法如下:<br /> <br /> <img alt="打包" class="img-thumbnail" src="/resources/assist/images/blog/df4fa9c18113451082e8f809bac9fba9.png" /><br /> <br /> maven打包后在target目录查看:<br /> <img alt="查看war包" class="img-thumbnail" src="/resources/assist/images/blog/f2e0549b4f534b4ba362d49d6357a231.png" />​​​​​​​
  • spring boot 导入本地jar包

    spring boot 导入本地jar包spring boot maven 打war包时候导入本地jar包spring boot 导入本地jar包spring boot maven 打war包时候导入本地jar包<br /> <br /> <br /> 首先一个纯净的spring boot war包项目的pom.xml如下: <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.leftso</groupId> <artifactId>lee-monitor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>lee-monitor</name> <description>cmd</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.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> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</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-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <!--启用本地jar包---> <configuration> <includeSystemScope>true</includeSystemScope> </configuration> </plugin> </plugins> </build> </project> </code></pre> <br /> <br /> 项目中创建一个lib目录,将本地jar包放入lib目录中:<br /> <img alt="lib目录" class="img-thumbnail" src="/resources/assist/images/blog/f53502417f7e4d0480bea7bb1e03ab62.png" /><br />   <pre>  </pre> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.leftso</groupId> <artifactId>lee-monitor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>lee-monitor</name> <description>cmd</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.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> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</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-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--本地包--> <dependency> <groupId>leftso</groupId> <artifactId>common</artifactId> <version>1.0</version> <scope>system</scope> <systemPath>${project.basedir}/lib/Common.jar</systemPath> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> </code></pre> 上面的操作已经可以将本地的jar包引入到spring boot项目中了<br /> <br /> <br /> 打包的时候讲本地jar包打入war中还需要添加一个war的打包插件,配置如下: <pre> <code class="language-xml"> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <webResources> <resource> <directory>${project.basedir}/lib</directory> <targetPath>WEB-INF/lib/</targetPath> <includes> <include>**/*.jar</include> </includes> </resource> </webResources> </configuration> </plugin></code></pre> <br /> 搞定<br />  
  • SpringMVC Maven打包war/jar没有Mapper.xml文件问题

    SpringMVC mybaties Maven打包war/jar没有Mapper.xml文件问题原因以及解决方法<img alt="maven项目结构" class="img-thumbnail" src="/resources/assist/images/blog/17182b8acf4f4b2e92f83fac01361e6a.png" /><br /> <br /> <span style="color:#1abc9c"><strong>xml是属于我们的资源文件,所以在src/main/java中是不被打包的,只有存在src/main/resources中才会进行打包。</strong></span><br />   <h4><strong>解决方法:</strong></h4> 1.将mapper.xml文件放在resources目录下<br /> 2.配置pom.xml打包信息 <pre> <code class="language-xml"><build> <finalName>xzbms</finalName> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> <include>**/*.tld</include> </includes> <!-- 这里是false,用true会报 数据库连接 错误 --> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> <include>**/*.tld</include> </includes> <filtering>false</filtering> </resource> </resources> </build></code></pre> <br /> <strong>意思就是不管在src/main/java或者是src/main/resources中都被打包。</strong>
  • idea2018配置Tomcat8热部署完整记录

    idea2018配置Tomcat8热部署完整记录    由于初步学习使用idea。所以工具用的还不是很熟。但是热部署这个东西,对于砸门开发人员来说还是非常必要的。经百度了了一下。发现真是写的参差不齐。就连百度经验里面的教程都是不全面的。导致小恼火了一把。特写一篇博客记录完整的配置方式供大家参考。<br />   <p>步骤一:添加Tomcat到idea中<br /> <img alt="添加Tomcat" class="img-thumbnail" src="/resources/assist/images/blog/bbce7b32569f4b0580e5eef5ca5e8ef4.png" /><br /> <br /> <img alt="添加Tomcat" class="img-thumbnail" src="/resources/assist/images/blog/48c1af1146d8474dbf456bb7d9aec17c.png" /><br /> <br /> 上图的+ -号就是添加和删除Tomcat。<br /> <br /> 步骤二:项目中Tomcat的配置<br /> <br /> 点击idea的右上角编辑配置<br /> <img alt="项目中的配置" class="img-thumbnail" src="/resources/assist/images/blog/8c6349b680d140d78477cff1107cb80d.png" /><br /> <br /> 选择Tomcat服务器(初次则这里点击上面的+号添加Tomcat服务器),然后选择Deployment.下方有+/-号添加项目。</p> <blockquote> <p>注意添加的项目会有两个选择一个是war结尾的一个是war exploded的。选择exploded的这个非常重要。选择不对则后面无法配置</p> </blockquote> <p><br /> <img alt="添加项目包到Tomcat中" class="img-thumbnail" src="/resources/assist/images/blog/67233e76fc504d5c9f3957d4b5a7a104.png" /><br /> <br /> <br /> 配置资源热部署<br /> <br /> 在On Update action和on frame deactivation两个配置中选择update class and resources完成配置。如果这里没看到update class and resources配置项。则检查上一步的项目部署包是否选择正确<br /> <img alt="配置资源热部署" class="img-thumbnail" src="/resources/assist/images/blog/b650aa09dbbe4d7ca8bfd15a11eeb0f6.png" /><br /> <br /> <br /> 好啦。完整的idea2018配置Tomcat8 热部署完成。<br />  </p>
  • spring boot使用Jenkins部署Spring Boot项目

    spring boot使用Jenkins部署Spring Boot项目<p>jenkins是devops神器,本篇文章介绍如何安装和使用jenkins部署Spring Boot项目</p> <p>jenkins搭建 部署分为三个步骤;</p> <ul> <li> <p>第一步,jenkins安装</p> </li> <li> <p>第二步,插件安装和配置</p> </li> <li> <p>第三步,Push SSH</p> </li> <li> <p>第四步,部署项目</p> </li> </ul> <h2>第一步 ,jenkins安装</h2> <p>准备环境:</p> <p>JDK:1.8<br /> Jenkins:2.83 Centos:7.3<br /> maven 3.5‘</p> <blockquote> <p>jdk默认已经安装完成</p> </blockquote> <h3>配置maven</h3> <p>版本要求maven3.5.0</p> <p>软件下载</p> <pre> <code class="language-html">wget http://mirror.bit.edu.cn/apache/maven/maven-3/3.5.0/binaries/apache-maven-3.5.0-bin.tar.gz</code></pre> <p>安装</p> <pre> <code class="language-bash">## 解压 tar vxf apache-maven-3.5.0-bin.tar.gz ## 移动 mv apache-maven-3.5.0 /usr/local/maven3</code></pre> <p>修改环境变量, 在/etc/profile中添加以下几行</p> <pre> <code class="language-html">MAVEN_HOME=/usr/local/maven3 export MAVEN_HOME export PATH=${PATH}:${MAVEN_HOME}/bin</code></pre> <p>记得执行 <code>source/etc/profile</code>使环境变量生效。</p> <p>验证 最后运行 <code>mvn-v</code>验证maven是否安装成功</p> <h3>配置防护墙</h3> <p>关闭防护墙</p> <pre> <code class="language-html">#centos7 systemctl stop firewalld.service ============================== #以下为:centOS 6.5关闭防火墙步骤 #关闭命令:   service iptables stop #永久关闭防火墙: chkconfig iptables off</code></pre> <p>两个命令同时运行,运行完成后查看防火墙关闭状态</p> <pre> <code class="language-html">service iptables status</code></pre> <h3>jenkins 安装</h3> <p>下载<br />  </p> <pre> <code class="language-html">cd /opt wget http://mirrors.jenkins.io/war/2.83/jenkins.war</code></pre> <p>启动服务</p> <pre> <code class="language-html">​​​​​​​java -jar jenkins.war &</code></pre> <p>Jenkins 就启动成功了!它的war包自带Jetty服务器</p> <p>第一次启动Jenkins时,出于安全考虑,Jenkins会自动生成一个随机的口令。<strong>注意控制台输出的口令,复制下来</strong>,然后在浏览器输入密码:</p> <pre> <code class="language-html">INFO: ************************************************************* ************************************************************* ************************************************************* Jenkins initial setup is required. An admin user has been created and a password generated. Please use the following password to proceed to installation: 0cca37389e6540c08ce6e4c96f46da0f This may also be found at: /root/.jenkins/secrets/initialAdminPassword ************************************************************* ************************************************************* *************************************************************</code></pre> <p>访问 浏览器访问: <code>http://localhost:8080/<br /> <img alt="安装" class="img-thumbnail" src="/resources/assist/images/blog/dc38e97e558148d1953ee91e9630bf50.jpg" /></code><br />  </p> <p>输入:0cca37389e6540c08ce6e4c96f46da0f</p> <p>进入用户自定义插件界面,建议选择安装官方推荐插件,因为安装后自己也得安装:<br /> <img alt="install 1" class="img-thumbnail" src="/resources/assist/images/blog/badf16beeea84c8a8b119fc7cf5e0d7f.jpg" /><br /> 接下来是进入插件安装进度界面:<br /> <img alt="install 2" class="img-thumbnail" src="/resources/assist/images/blog/c5901b1554cc476a87d2806b048d3c8f.jpg" /><br /> 插件一次可能不会完全安装成功,可以点击Retry再次安装,直到全部安装成功。<br /> <img alt="install 3" class="img-thumbnail" src="/resources/assist/images/blog/f88e983c46a746fb9ab1f7adc3c1f35c.jpg" /><br /> 等待一段时间之后,插件安装完成,配置用户名密码:<br /> <img alt="install 5" class="img-thumbnail" src="/resources/assist/images/blog/d99345eac5e44be7bf9573da39832f99.jpg" /><br />  </p> <p>输入:admin/admin</p> <p>系统管理-》全局工具配置 jdk路径,<br /> <img alt="install 6" class="img-thumbnail" src="/resources/assist/images/blog/049f7d98e1e64175919eb75eefd662f0.jpg" /></p> <h2>第二步,插件安装和配置</h2> <p>有很多插件都是选择的默认的安装的,所以现在需要我们安装的插件不多,Git plugin和Maven Integration plugin,publish over SSH。</p> <p>插件安装:系统管理 > 插件管理 > 可选插件,勾选需要安装的插件,点击直接安装或者下载重启后安装<br /> <img alt="install 7" class="img-thumbnail" src="/resources/assist/images/blog/c4944230d3fb4b668c5f606441d2575f.jpg" /></p> <h3>配置全局变量</h3> <p>系统管理 > 全局工具配置</p> <p><strong>JDK</strong></p> <p>配置本地JDK的路径,去掉勾选自动安装<br /> <img alt="install 8" class="img-thumbnail" src="/resources/assist/images/blog/590fb0fbbb7047df96ea276960438285.png" /><br /> <br />  </p> <p><strong>Maven</strong></p> <p>配置本地maven的路径,去掉勾选自动安装<br /> <img alt="install 9" class="img-thumbnail" src="/resources/assist/images/blog/deb59e78535c45fe8bb914b592491e6a.png" /></p> <p>其它内容可以根据自己的情况选择安装。</p> <h3>配置 SSH免登陆</h3> <p>ssh的配置可使用密钥,也可以使用密码,这里我们使用密钥来配置,在配置之前先配置好jenkins服务器和应用服务器的密钥认证 <strong>jenkins服务器</strong>上生成密钥对,使用 <code>ssh-keygen-t rsa</code>命令</p> <p>输入下面命令 一直回车,一个矩形图形出现就说明成功,在~/.ssh/下会有私钥idrsa和公钥idrsa.pub</p> <pre> <code class="language-html">ssh-keygen -t rsa</code></pre> <p>将<strong>jenkins服务器</strong>的公钥 <code>id_rsa.pub</code>中的内容复制到<strong>应用服务器</strong> 的~/.ssh/下的 <code>authorized_keys</code>文件</p> <pre> <code class="language-html">ssh-copy-id -i id_rsa.pub 192.168.0.xx chmod 644 authorized_keys</code></pre> <p>在<strong>应用服务器</strong>上重启ssh服务, <code>service sshd restart</code>现在jenkins服务器可免密码直接登陆应用服务器</p> <p>之后在用ssh B尝试能否免密登录B服务器,如果还是提示需要输入密码,则有以下原因</p> <ul> <li>a. 非root账户可能不支持ssh公钥认证(看服务器是否有限制)</li> <li>b. 传过来的公钥文件权限不够,可以给这个文件授权下 chmod 644 authorized_keys</li> <li>c. 使用root账户执行ssh-copy-id -i ~/.ssh/idrsa.pub 这个指令的时候如果需要输入密码则要配置sshdconfig<br />  </li> </ul> <pre> <code class="language-html">vi /etc/ssh/sshd_config #内容 PermitRootLogin no</code></pre> <p>修改完后要重启sshd服务</p> <pre> <code class="language-html">service sshd restart</code></pre> <p>最后,如果可以SSH IP 免密登录成功说明SSH公钥认证成功。</p> <h2>第三步,Push SSH</h2> <p>系统管理 > 系统设置</p> <p>选择 Publish over SSH<br /> <img alt="install 10" class="img-thumbnail" src="/resources/assist/images/blog/d7cb7603ce754991b4545d037723c95b.jpg" /><br />  </p> <p>Passphrase 不用设置 Path to key 写上生成的ssh路径: <code>/root/.ssh/id_rsa</code></p> <p>下面的SSH Servers是重点</p> <p>Name 代表这个服务的名字,待会要根据它来选择 Hostname 配置应用服务器的地址 ,Username 配置linux登陆用户名,Remote Directory 不填。</p> <blockquote> <p>点击下方增加可以添加多个应用服务器的地址</p> </blockquote> <h2>第四步,部署项目</h2> <p>首页点击<strong>新建</strong>:输入项目名称<br /> <img alt="install 11" class="img-thumbnail" src="/resources/assist/images/blog/6839c4c2b4fc459997b8da3358ba5ab5.jpg" /><br />  </p> <p>下方选择构建一个maven项目,点击确定。</p> <p>勾选<strong>丢弃旧的构建</strong>,选择是否备份被替换的旧包。我这里选择备份最近的10个<br /> <img alt="install2" class="img-thumbnail" src="/resources/assist/images/blog/7b39a1d66e2141bb85be6ab5b06af713.jpg" />源码管理,选择svn,配置SVN相关信息,点击add可以输入svn的账户和密码<br /> <img alt="install 13" class="img-thumbnail" src="/resources/assist/images/blog/b56df59efbe846858d6246d1d3aadcef.jpg" /></p> <p>svn地址:http://192.168.0.xx/svn/xxx@HEAD, <code>@HEAD</code>意思取最新版本</p> <p>构建环境中勾选“Add timestamps to the Console Output”,代码构建的过程中会将日志打印出来<br /> <img alt="install 14" class="img-thumbnail" src="/resources/assist/images/blog/161fd6e0dab344febfd77e51840044aa.png" /><br />  </p> <p>在Build中输入打包前的mvn命令,如:</p> <pre> <code class="language-html">clean install -Dmaven.test.skip=true -Ptest</code></pre> <p>意思是:排除测试的包内容,使用后缀为test的配置文件。<br /> <img alt="install 15" class="img-thumbnail" src="/resources/assist/images/blog/3fb887bd57584b6ea9741242c227beeb.png" /><br /> Post Steps 选择 Run only if build succeeds<br /> <img alt="install 16" class="img-thumbnail" src="/resources/assist/images/blog/1fb7cbbb0a2b47e4a51984fad847011c.png" /><br /> 点击<strong>Add post-build step</strong>,选择 Send files or execute commands over SSH<br /> <img alt="install 17" class="img-thumbnail" src="/resources/assist/images/blog/7d0297473cb74033951da9881d7f7618.jpg" />Name选择上面配置的Push SSH<br /> <img alt="install 18" class="img-thumbnail" src="/resources/assist/images/blog/bba4530a46d64d30bf4e47cc48850625.jpg" /><br />  </p> <p>Source files配置:target/xxx-0.0.1-SNAPSHOT.jar 项目jar包名 ;Remove prefix:target/ ;Remote directory:Jenkins-in/ 代码应用服务器的目录地址;Exec command:Jenkins-in/xxx.sh 应用服务器对应的脚本。</p> <p>需要在应用服务器创建文件夹:Jenkins-in,在文件夹中复制一下脚本内容:xxx.sh<br />  </p> <pre> <code class="language-html">DATE=$(date +%Y%m%d) export JAVA_HOME PATH CLASSPATH JAVA_HOME=/usr/java/jdk1.8.0_131 PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH DIR=/root/xxx JARFILE=xxx-0.0.1-SNAPSHOT.jar if [ ! -d $DIR/backup ];then   mkdir -p $DIR/backup fi cd $DIR ps -ef | grep $JARFILE | grep -v grep | awk '{print $2}' | xargs kill -9 mv $JARFILE backup/$JARFILE$DATE mv -f /root/Jenkins-in/$JARFILE . java -jar $JARFILE > out.log & if [ $? = 0 ];then        sleep 30        tail -n 50 out.log fi cd backup/ ls -lt|awk 'NR>5{print $NF}'|xargs rm -rf</code></pre>
  • Spring Boot中使用jsp视图模板-Java编程之

    Java编程之Spring Boot中使用jsp视图模板<h2>本文简介</h2> 本文将学习怎么创建spring boot项目并在spring boot项目中使用JSP模板作为视图层。这里会使用嵌入的tomcat server去运行这个程序。 <h2>一、项目结构图</h2> <img alt="项目结构图" class="img-thumbnail" src="/resources/assist/images/blog/76c1ff4b065246e3aac10181ea66430e.png" /> <h2>二、maven依赖文件pom.xml</h2> 这个应用将会使用以下依赖: <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.howtodoinjava</groupId> <artifactId>spring-boot-demo</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-demo Maven Webapp</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Tomcat Embed --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!-- JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- To compile JSP files --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> </dependencies> </project></code></pre> <h2>三、spring boot应用的初始类</h2>   首先创建一个可部署的war文件的第一步是提供一个SpringBootServletInitializer子类和覆盖其配置()方法。这将使用Spring Framework的Servlet 3.0支持,并允许您在Servlet容器启动时配置您的应用程序。 <pre> <code class="language-java">import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; @SpringBootApplication public class SpringBootWebApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBootWebApplication.class); } public static void main(String[] args) throws Exception { SpringApplication.run(SpringBootWebApplication.class, args); } }</code></pre> <h2><br /> 四、Spring Controller</h2> 控制器类可以将方法映射到应用程序中的特定url。在给定的应用程序中,它有两个视图,“/”和“/next” <pre> <code class="language-java">import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { @RequestMapping("/") public String home(Map<String, Object> model) { model.put("message", "HowToDoInJava Reader !!"); return "index"; } @RequestMapping("/next") public String next(Map<String, Object> model) { model.put("message", "You are in new page !!"); return "next"; } }</code></pre> <h2>五、配置JSP视图解析器</h2> 解析jsp文件有两种方法可行<br /> 5.1在application.properties中添加以下内容:<br />   <pre> <code>spring.mvc.view.prefix=/WEB-INF/view/ spring.mvc.view.suffix=.jsp //For detailed logging during development logging.level.org.springframework=TRACE logging.level.com=TRACE</code></pre> <br /> 5.2配置InternalResourceViewResolver <pre> <code class="language-java">import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration @EnableWebMvc @ComponentScan public class MvcConfiguration extends WebMvcConfigurerAdapter { @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/view/"); resolver.setSuffix(".jsp"); resolver.setViewClass(JstlView.class); registry.viewResolver(resolver); } }</code></pre> <h2><br /> 六、JSP文件编写</h2> <strong>index.jsp</strong> <pre> <code class="language-html"><!DOCTYPE html> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <html lang="en"> <body> <div> <div> <h1>Spring Boot JSP Example</h1> <h2>Hello ${message}</h2> Click on this <strong><a href="next">link</a></strong> to visit another page. </div> </div> </body> </html></code></pre> <strong>next.jsp</strong> <pre> <code class="language-html"><!DOCTYPE html> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <html lang="en"> <body> <div> <div> <h1>Another page</h1> <h2>Hello ${message}</h2> Click on this <strong><a href="/">link</a></strong> to visit previous page. </div> </div> </body> </html></code></pre> <h2>七、运行程序</h2> 在将整个代码编写并放置在文件夹中之后,通过在<code>SpringBootWebApplication</code> 类中执行main()方法来运行应用程序<br /> <br /> 打开浏览器:http://localhost:8080/<br /> <img alt="index" class="img-thumbnail" src="/resources/assist/images/blog/d961b6f45c7142039956ab5b0c411468.png" /><br /> 点击next<br /> <img alt="next" class="img-thumbnail" src="/resources/assist/images/blog/9d25d5a067434b51bb11015b822cce28.png" /> <h2>八、例子下载</h2> <a href="http://howtodoinjava.com/wp-content/downloads/spring-boot-demo-jsp-example.zip" rel="external nofollow" target="_blank">点击下载</a>
  • Spring MVC 5.0

    Spring框架5.0,spring mvc 5.0入门教程。DispatcherServlet的详细讲解配置以及spring mvc5.0的helloword程序<h2>引言</h2>   在本教程中,我们将学习Spring DispatcherServlet类,其职责以及如何使用示例进行配置。spring mvc 5.0基于Java配置 <h2>一.DispatcherServlet是什么</h2> <br />     <code>DispatcherServlet</code> 充当基于Spring的Web应用程序的前端控制器。 它提供了一个请求处理机制,其中实际工作由可配置的委托组件执行。 它从javax.servlet.http.HttpServlet继承,通常在web.xml文件中配置。<br /> <br />     Web应用程序可以定义任意数量的<code>DispatcherServlet</code> 实例。 每个servlet将在其自己的名称空间中运行,使用映射,处理程序等加载它自己的应用程序上下文。只有由ContextLoaderListener加载的根应用程序上下文(如果有)将被共享。 在大多数情况下,应用程序只有一个具有上下文根URL(/)的<code>DispatcherServlet</code> ,也就是说,到达该域的所有请求都将由它处理。<br /> <br />     <code>DispatcherServlet</code> 使用Spring配置类来发现它需要的委托组件,用于请求映射,视图解析,异常处理等。 <h2>二.DispatcherServlet它如何使用WebApplicationContext</h2>     在基于Spring的应用程序中,我们的应用程序对象位于一个对象容器中。 该容器在对象之间创建对象和关联,并管理其完整的生命周期。 这些容器对象被称为Spring管理的bean(或者简单的bean),在Spring世界中容器被称为应用程序上下文(通过类ApplicationContext)。<br /> <br />     WebApplicationContext是一个普通的ApplicationContext的扩展。 它是Web感知的ApplicationContext,即它具有Servlet上下文信息。 当DispatcherServlet被加载时,它查找WebApplicationContext的bean配置文件并初始化它。<br /> <br />     通过访问Servlet上下文,任何实现了ServletConextAware接口的spring bean都可以访问ServletContext实例,并且可以做很多事情。 例如,它可以获取上下文初始化参数,获取上下文根信息并获取Web应用程序文件夹中的资源位置。 <h2>三.基于XML配置的DispatcherServlet(作为参考理解主讲基于Java配置)</h2> 我们来看看典型的DispatcherServlet声明和初始化是怎样的。 <pre> <code class="language-xml"><web-app> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet-context.xml</param-value> </context-param> <servlet> <servlet-name>dispatcher-servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher-servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app></code></pre> 在上面的代码中,dispatcher-servlet-context.xml文件将包含所有可用于DispatcherServlet的bean定义和关联。 这些bean定义将覆盖在全局范围内使用相同名称定义的任何bean的定义。 例如: <pre> <code class="language-xml"><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name="prefix"> <value>/WEB-INF/views/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans></code></pre> <h2>四.基于JAVA代码配置的DispatcherServlet </h2> 从Servlet 3.0开始,除了web.xml文件中的声明式配置,DispatcherServlet可以通过实现或扩展Spring提供的这三个支持类来编程配置  <ul> <li>WebAppInitializer接口</li> <li>AbstractDispatcherServletInitializer抽象类</li> <li>AbstractAnnotationConfigDispatcherServletInitializer抽象类</li> </ul> 在下面的类中,WebApplicationInitializer确保ApplicationSerializer类被SpringServletContainerInitializer(它本身自动引导)检测到并用来初始化任何Servlet 3容器。<br /> <br /> 引入spring mvc 5.0需要的相关依赖,pom.xml: <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.leftso.project.demo</groupId> <artifactId>demo-spring5-springmvc5</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <failOnMissingWebXml>false</failOnMissingWebXml> <spring.version>5.0.1.RELEASE</spring.version> <jstl.version>1.2.1</jstl.version> <tld.version>1.1.2</tld.version> <servlets.version>3.1.0</servlets.version> <jsp.version>2.3.1</jsp.version> </properties> <dependencies> <!-- Spring MVC Dependency --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- JSTL Dependency --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>javax.servlet.jsp.jstl-api</artifactId> <version>${jstl.version}</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>${tld.version}</version> </dependency> <!-- Servlet Dependency --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlets.version}</version> <scope>provided</scope> </dependency> <!-- JSP Dependency --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>${jsp.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <sourceDirectory>src/main/java</sourceDirectory> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <path>/</path> </configuration> </plugin> </plugins> </build> </project></code></pre> <br /> <br /> 通过Java的方式可以将上面的web.xml写一个配置类: <pre> <code class="language-java">public class ApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { XmlWebApplicationContext appContext = new XmlWebApplicationContext(); appContext.setConfigLocation("/WEB-INF/dispatcher-servlet-context.xml"); ServletRegistration.Dynamic registration = servletContext .addServlet("rootDispatcher", new DispatcherServlet(appContext)); registration.setLoadOnStartup(1); registration.addMapping("/"); } }</code></pre> 基于Java 配置的完整配置<br /> <strong>例子一:</strong> <pre> <code class="language-java">package com.leftso.project.demo.config; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public class ApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { // 创建 Spring ROOT context AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(AppConfig.class); // 管理ROOT context生命周期 servletContext.addListener(new ContextLoaderListener(rootContext)); // 创建spring的dispatcher ,servlet's Spring application context AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext(); dispatcherContext.register(DispatcherConfig.class); ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherContext)); dispatcher.setLoadOnStartup(1); dispatcher.addMapping("/"); } } </code></pre> 在上面的代码中,<code>AppConfig</code> 和<code>DispatcherConfig</code> 类定义了将在Web应用程序上下文中的spring管理的bean。<br /> AppConfig: <pre> <code class="language-java">package com.leftso.project.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import com.leftso.project.demo.TestService; @Configuration @ComponentScan(basePackages = { "com.leftso" }) public class AppConfig { // 该类主要是spring 父级容器配置,里面可以定义很多的bean就像在spring-context.xml中一样 // 例如: @Bean public TestService testService() { return new TestService(); } } </code></pre> DispatcherConfig: <pre> <code class="language-java">package com.leftso.project.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration public class DispatcherConfig extends WebMvcConfigurationSupport { // 覆盖父类的配置进行spring mvc的其他详细配置,配置内容可参考xml的spring-mvc /** * * <pre> * [Function]: * JSP 视图解析器 * [Description]: * JSP 视图解析器 * </pre> * * @return JSP 视图解析器 */ @Bean public ViewResolver jspViewResolver() { InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setViewClass(JstlView.class); internalResourceViewResolver.setSuffix(".jsp"); internalResourceViewResolver.setPrefix("/WEB-INF/views/"); internalResourceViewResolver.setOrder(4); return internalResourceViewResolver; } /** * 资源文件处理器 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); } } </code></pre> <p><strong>例子二:</strong><br /> 基于Java配置的Spring MVC 5.0的配置,spring mvc 5.0为我们提供了一个封装的抽象类,我们实现这个类并指定相关的配置Java就可以了非常方便:</p> <pre> <code class="language-java">package com.leftso.project.demo.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; /** * Spring MVC 5启动配置 * * @author xqlee * */ public class SpringMVC5AnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] {}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebMvcConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } </code></pre> <p>WebMvcConfig:<br />  </p> <pre> <code class="language-java">package com.leftso.project.demo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.leftso.project.demo" }) public class WebMvcConfig implements WebMvcConfigurer { @Bean public InternalResourceViewResolver resolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setViewClass(JstlView.class); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; } } </code></pre> <p>这比起第一种方式少些几句代码。</p> <h2><br /> 五.编写一个测试spring mvc 5.0的controller</h2> <pre> <code class="language-java">package com.leftso.project.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping("/sayHello.do") public Object sayHello(String name) { return "Hello," + name; } } </code></pre> 将项目添加到tomcat中,启动tomcat,并在浏览器中访问controller的地址:<br /> <img alt="spring mvc 5.0" class="img-thumbnail" src="/resources/assist/images/blog/e2422615e7924625a55ad38461e09529.png" /><br /> 从上图可以看到我们的<a rel="" target="_blank"href="http://www.leftso.com/blog/305.html" rel="" target="_blank">spring mvc5.0</a>项目已经成功启动.后续将讲解整合其他配置<br />  
  • spring boot elasticsearch 5.x/6.x版本整合详解

    本教程主要讲解spring boot如何整合 spring data elasticsearch 实现elasticsearch检索引擎的整合使用。需要注意的是spring boot 1.x就算当前最新的1.5.11支持的elasticsearch版本仅为2.x。如果需要spring boot支持5.x版本的elasticsearch或者6.x版本的elasticsearch则需要将spring boot版本升级到最新的2.0.1即可。本教程所使用的elasticsearch版本为6.2。所以使用的spring boot版本将会是2.0.1<h2>前言</h2>     本教程主要讲解spring boot如何整合 spring data elasticsearch 实现elasticsearch检索引擎的整合使用。需要注意的是spring boot 1.x就算当前最新的1.5.11支持的elasticsearch版本仅为2.x。如果需要spring boot支持5.x版本的elasticsearch或者6.x版本的elasticsearch则需要将spring boot版本升级到最新的2.0.1即可。本教程所使用的elasticsearch版本为6.2。所以使用的spring boot版本将会是2.0.1 <h2>spring boot elasticsearch整合项目结构图</h2> <img srcset="" width="" size="" class="img-thumbnail" alt="spring boot elasticsearch整合项目结构图" src="/resources/assist/images/blog/aa80ef737a8241f3af7722887f3cc80f.png" /> <h2>Spring Boot elasticsearch整合项目文件详解</h2> <h3>1.pom.xml文件</h3> 由于本项目使用的maven管理。所以这里就只能看到pom.xml依赖了。相信你熟悉其他的管理方式也能看懂依赖。 <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>demo-springboot-springdata-elasticsearch</artifactId> <packaging>jar</packaging> <url>https://www.leftso.com</url> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--HTTP方式接入--> <!-- <dependency> <groupId>io.searchbox</groupId> <artifactId>jest</artifactId> </dependency>--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- Package as an executable jar/war --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project></code></pre> 核心依赖: <pre> <code class="language-xml"> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> </code></pre> <h3>Book.java</h3> 这个类主要用于创建检索的对象实体。 <pre> <code class="language-java">package com.leftso.project.demo.book.model; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; @Document(indexName = "lee", type = "books") public class Book { @Id private String id; private String title; private String author; private String releaseDate; public Book() { } public Book(String id, String title, String author, String releaseDate) { this.id = id; this.title = title; this.author = author; this.releaseDate = releaseDate; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getReleaseDate() { return releaseDate; } public void setReleaseDate(String releaseDate) { this.releaseDate = releaseDate; } @Override public String toString() { return "Book{" + "id='" + id + '\'' + ", title='" + title + '\'' + ", author='" + author + '\'' + ", releaseDate='" + releaseDate + '\'' + '}'; } } </code></pre> <h3>BookRepository.java</h3> 该接口主要继承了spring data的接口。实现了语义即可实现功能。spring data的强大大家都懂。 <pre> <code class="language-java">package com.leftso.project.demo.book.repository; import com.leftso.project.demo.book.model.Book; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface BookRepository extends ElasticsearchRepository<Book, String> { Page<Book> findByAuthor(String author, Pageable pageable); Page<Book> findByTitle(String title, Pageable pageable); }</code></pre> <h3>BookService.java</h3> 该接口主要是写了几个测试的方法。 <pre> <code class="language-java">package com.leftso.project.demo.book.service; import com.leftso.project.demo.book.model.Book; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; public interface BookService { Book save(Book book); void delete(Book book); Book findOne(String id); Iterable<Book> findAll(); Page<Book> findByAuthor(String author, PageRequest pageRequest); Page<Book> findByTitle(String title, PageRequest pageRequest); }</code></pre> <h3>spring boot 启动类Application.java</h3> <pre> <code class="language-java">package com.leftso.project.demo; import com.leftso.project.demo.book.model.Book; import com.leftso.project.demo.book.service.BookService; import org.elasticsearch.client.Client; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import java.util.Map; @SpringBootApplication public class Application implements CommandLineRunner { @Autowired private ElasticsearchOperations es; @Autowired private BookService bookService; public static void main(String args[]) { SpringApplication.run(Application.class, args); } @Override public void run(String... args) throws Exception { printElasticSearchInfo(); Book b1=bookService.save(new Book("1001", "这是一个Spring Boot1.0的书籍", "Rambabu Posa", "23-FEB-2017")); Book b2=bookService.save(new Book("1002", "Apache的一本书籍", "Rambabu Posa", "13-MAR-2017")); bookService.save(new Book("1003", "Solr是一个搜索引擎", "Rambabu Posa", "21-MAR-2017")); //fuzzey search // Page<Book> books = bookService.findByAuthor("Rambabu", new PageRequest(0, 10)); Page<Book> books = bookService.findByTitle("书", new PageRequest(0, 10)); books.forEach(x -> System.out.println(x)); } //useful for debug private void printElasticSearchInfo() { System.out.println("--ElasticSearch-->"); Client client = es.getClient(); Map<String, String> asMap = client.settings().getAsMap(); asMap.forEach((k, v) -> { System.out.println(k + " = " + v); }); System.out.println("<--ElasticSearch--"); } }</code></pre> 启动类实现了一个CommandLineRunner接口。该接口主要有个run方法。启动spring boot的时候会执行run方法的内容。就当测试啦。<br />   <h2>spring boot elasticsearch整合测试</h2> 鼠标右键启动application.java即可:<br /> <img srcset="" width="" size="" class="img-thumbnail" alt="启动测试" src="/resources/assist/images/blog/ca028d7a24044746ab7a6ed9cad0cc55.png" /><br /> <br /> 查看下方的执行情况:<br /> <img srcset="" width="" size="" class="img-thumbnail" alt="执行情况1" src="/resources/assist/images/blog/5ccb3b73c511421a9b971ee2f17aa191.png" /><br /> 从上图可以看到已经连接到elasticsearch 192.168.0.225:3000服务那边去了<br /> <img srcset="" width="" size="" class="img-thumbnail" alt="执行情况2" src="/resources/assist/images/blog/2616c579db8e41829729cd5d653cc037.png" /><br /> 从上图可以看到我们已经存入elasticsearch 并且通过spring data elasticsearch 的查询接口查询出了Book title字段中包含“书”的所有结果。 <blockquote> <p>提示:<a href="http://www.leftso.com/resource/1014.html" target="_blank" >项目源码下载</a></p> </blockquote>
  • spring boot 文件上传 REST风格API ajax方式-Java编程

    Java编程之Spring Boot 文件上传 REST风格API ajax方式<h2>一、摘要说明</h2>   这篇文章将告诉你在spring boot(REST 风格)WEB项目中如何用ajax请求上传文件。<br /> <br />   本文章中需要使用的工具: <ol> <li>Spring Boot 1.4.3.RELEASE</li> <li>Spring 4.3.5.RELEASE</li> <li>Thymeleaf</li> <li>jQuery (webjars)</li> <li>Maven</li> <li>Embedded Tomcat 8.5.6</li> <li>Google Chrome 浏览器(Network Inspect)</li> </ol> <h2>二、项目结构</h2> 一个标准的maven项目结构<br /> <img alt="项目结构" class="img-thumbnail" src="/resources/assist/images/blog/811600c9be364e56b3c68ebbe78e0b07.png" /> <h2>三、项目依赖</h2> 声明一个外部的jQuery webjar依赖项,用于HTML表单中的Ajax请求。<br /> <br /> <strong>pom.xml</strong> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mkyong</groupId> <artifactId>spring-boot-file-upload</artifactId> <packaging>jar</packaging> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- hot swapping, disable cache for template, enable live reload --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>2.2.4</version> </dependency> </dependencies> <build> <plugins> <!-- Package as an executable jar/war --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project></code></pre> <h2>四、文件上传</h2> 为了支持AJAX请求和响应, 最简单的方式是返回<code>ResponseEntity</code>. <h2>4.1下面的例子演示了上传文件的三种可能方式:</h2> <ol> <li>单个文件上传 – <code>MultipartFile</code></li> <li>多个文件上传– <code>MultipartFile[]</code></li> <li>Map file upload to a Model – <code>@ModelAttribute</code></li> </ol> <strong>RestUploadController.java</strong> <pre> <code class="language-java">import com.mkyong.model.UploadModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @RestController public class RestUploadController { private final Logger logger = LoggerFactory.getLogger(RestUploadController.class); //Save the uploaded file to this folder private static String UPLOADED_FOLDER = "F://temp//"; // 3.1.1 Single file upload @PostMapping("/api/upload") // If not @RestController, uncomment this //@ResponseBody public ResponseEntity<?> uploadFile( @RequestParam("file") MultipartFile uploadfile) { logger.debug("Single file upload!"); if (uploadfile.isEmpty()) { return new ResponseEntity("please select a file!", HttpStatus.OK); } try { saveUploadedFiles(Arrays.asList(uploadfile)); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity("Successfully uploaded - " + uploadfile.getOriginalFilename(), new HttpHeaders(), HttpStatus.OK); } // 3.1.2 Multiple file upload @PostMapping("/api/upload/multi") public ResponseEntity<?> uploadFileMulti( @RequestParam("extraField") String extraField, @RequestParam("files") MultipartFile[] uploadfiles) { logger.debug("Multiple file upload!"); // Get file name String uploadedFileName = Arrays.stream(uploadfiles).map(x -> x.getOriginalFilename()) .filter(x -> !StringUtils.isEmpty(x)).collect(Collectors.joining(" , ")); if (StringUtils.isEmpty(uploadedFileName)) { return new ResponseEntity("please select a file!", HttpStatus.OK); } try { saveUploadedFiles(Arrays.asList(uploadfiles)); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity("Successfully uploaded - " + uploadedFileName, HttpStatus.OK); } // 3.1.3 maps html form to a Model @PostMapping("/api/upload/multi/model") public ResponseEntity<?> multiUploadFileModel(@ModelAttribute UploadModel model) { logger.debug("Multiple file upload! With UploadModel"); try { saveUploadedFiles(Arrays.asList(model.getFiles())); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity("Successfully uploaded!", HttpStatus.OK); } //save file private void saveUploadedFiles(List<MultipartFile> files) throws IOException { for (MultipartFile file : files) { if (file.isEmpty()) { continue; //next pls } byte[] bytes = file.getBytes(); Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename()); Files.write(path, bytes); } } }</code></pre> <h2>4.2选择4.1中的第三种方式上传文件并且使用注解@ModelAttribute</h2> <strong>UploadModel.java</strong> <pre> <code class="language-java">import org.springframework.web.multipart.MultipartFile; public class UploadModel { private String extraField; private MultipartFile[] files; //getters and setters }</code></pre> <h2>五、视图文件的编写</h2> HTML form for multiple file uploads.<br /> <strong>upload.html</strong> <pre> <code class="language-html"><!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <body> <h1>Spring Boot - Multiple file upload example - AJAX</h1> <form method="POST" enctype="multipart/form-data" id="fileUploadForm"> <input type="text" name="extraField"/><br/><br/> <input type="file" name="files"/><br/><br/> <input type="file" name="files"/><br/><br/> <input type="submit" value="Submit" id="btnSubmit"/> </form> <h1>Ajax Post Result</h1> <pre> <span id="result"></span> </pre> <script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script> <script type="text/javascript" src="js/main.js"></script> </body> </html></code></pre> <h2>六、jQuery – Ajax Request</h2> jQuery通过f<code>#id获取form</code>,并且发送multipart form 数据通过ajax请求.<br /> <strong>resources/static/js/main.js</strong> <pre> <code class="language-javascript">$(document).ready(function () { $("#btnSubmit").click(function (event) { //stop submit the form, we will post it manually. event.preventDefault(); fire_ajax_submit(); }); }); function fire_ajax_submit() { // Get form var form = $('#fileUploadForm')[0]; var data = new FormData(form); data.append("CustomField", "This is some extra data, testing"); $("#btnSubmit").prop("disabled", true); $.ajax({ type: "POST", enctype: 'multipart/form-data', url: "/api/upload/multi", data: data, //http://api.jquery.com/jQuery.ajax/ //https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects processData: false, //prevent jQuery from automatically transforming the data into a query string contentType: false, cache: false, timeout: 600000, success: function (data) { $("#result").text(data); console.log("SUCCESS : ", data); $("#btnSubmit").prop("disabled", false); }, error: function (e) { $("#result").text(e.responseText); console.log("ERROR : ", e); $("#btnSubmit").prop("disabled", false); } }); }</code></pre> <h2>七、Exception Handler异常拦截</h2> 拦截来自 Ajax请求的异常, 只需要继承 <code>ResponseEntityExceptionHandler</code> 并且返回 <code>ResponseEntity</code>.<br /> <strong>RestGlobalExceptionHandler.java</strong> <pre> <code class="language-java">import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import javax.servlet.http.HttpServletRequest; //http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling @ControllerAdvice public class RestGlobalExceptionHandler extends ResponseEntityExceptionHandler { // Catch file size exceeded exception! @ExceptionHandler(MultipartException.class) @ResponseBody ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) { HttpStatus status = getStatus(request); return new ResponseEntity(ex.getMessage(), status); // example //return new ResponseEntity("success", responseHeaders, HttpStatus.OK); } private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } return HttpStatus.valueOf(statusCode); } }</code></pre> <h2>八、演示</h2> 使用默认的嵌入式Tomcat启动Spring启动<code>mvn spring-boot:run</code>.<br /> 8.1访问http://localhost:8080/,选择几个文件并单击submit以触发ajax请求。<br /> <img alt="访问并上传" class="img-thumbnail" src="/resources/assist/images/blog/19d6d8c691d04a079fd5a18adc7560fa.png" /><br /> 8.2 Google Chrome浏览器,检查“网络检查”中的请求和响应<br /> <img alt="观察请求" class="img-thumbnail" src="/resources/assist/images/blog/ae51363c32cf415b943dbdc57d659470.png" /><br /> 8.4 Google Chrome, “Request Payload”<br /> <img alt="上传内容" class="img-thumbnail" src="/resources/assist/images/blog/7f1ff5d5dde64d89ae7afd664c5df0f5.png" /> <h2>9.curl工具测试</h2> More testing with <code>cURL</code> command.<br /> 9.1测试单个文件上传 <pre> <code>$ curl -F file=@"f:\\data.txt" http://localhost:8080/api/upload/ Successfully uploaded - data.txt</code></pre> <br /> 9.2.测试多个文件上传 <pre> <code>$ curl -F extraField="abc" -F files=@"f://data.txt" -F files=@"f://data2.txt" http://localhost:8080/api/upload/multi/ Successfully uploaded - data.txt , data2.txt</code></pre> 9.3测试多个文件上传使用maps to Model模式 <pre> <code>$ curl -F extraField="abc" -F files=@"f://data.txt" -F files=@"f://data2.txt" http://localhost:8080/api/upload/multi/model Successfully uploaded! </code></pre> <br /> 9.4测试一个大文件(超过100MB),将会提示下面的错误信息 <pre> <code>$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/ Attachment size exceeds the allowable limit! (10MB)</code></pre> <h2>十、curl测试加自定义错误对象测试</h2> 10.1创建一个自定义对象去存放异常信息<br /> <strong>CustomError.java</strong> <pre> <code class="language-java">public class CustomError { String errCode; String errDesc; public CustomError(String errCode, String errDesc) { this.errCode = errCode; this.errDesc = errDesc; } //getters and setters }</code></pre> 10.2更新全局的异常拦截支持自定义异常信息<br /> <strong>RestGlobalExceptionHandler.java</strong> <pre> <code class="language-java">import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import javax.servlet.http.HttpServletRequest; @ControllerAdvice public class RestGlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(MultipartException.class) @ResponseBody ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) { HttpStatus status = getStatus(request); return new ResponseEntity(new CustomError("0x000123", "Attachment size exceeds the allowable limit! (10MB)"), status); //return new ResponseEntity("Attachment size exceeds the allowable limit! (10MB)", status); } private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } return HttpStatus.valueOf(statusCode); } }</code></pre> <br /> 10.3再次上传一个大文件 <pre> <code>$ curl -F file=@"F://movies//300//Sample.mkv" http://localhost:8080/api/upload/ {"errCode":"0x000123","errDesc":"Attachment size exceeds the allowable limit! (10MB)"}</code></pre> <br /> <a href="http://www.mkyong.com/wp-content/uploads/2017/01/spring-boot-file-upload-ajax-rest.zip" rel="external nofollow" target="_blank">demo项目下载</a>
  • 使用OAuth2安全的Spring security REST API(含demo代码下载)

    使用OAuth2安全的Spring REST API,Secure Spring REST API using OAuth2(含demo代码下载)<p>    让我们<code>Spring REST API</code>使用<code>OAuth2</code>这个时间来保护我们,这是一个简单的指南,显示使用REST API来保护REST API所需的内容<code>Spring OAuth2</code>。我们的用例符合<code>Resource-owner Password Grant</code>OAUth2规范的流程。我们将使用两个不同的客户端(Postman和<code>Spring RestTemplate</code>基于Java 的应用程序)访问我们的OAuth2受保护的REST资源。</p> <p>    如果您已经熟悉OAuth2概念,您可能希望跳过该理论,并直接跳转到代码中。一如以往,在本文末尾附件中可以找到完整的代码。让我们开始吧。<br />  </p> <h3>    什么是OAuth2</h3> <p>    OAuth2是一个标准化的授权协议/框架。根据官方OAuth2规范:</p> <p>    OAuth 2.0授权框架使得第三方应用程序可以通过协调资源所有者和HTTP服务之间的批准交互来代替资源所有者来<u>获取</u>对HTTP服务的<u>有限访问权限</u>,也可以允许第三方应用程序以自己的身份获得访问权限。</p> <p>    Google,Facebook等大型玩家已经在使用自己的OAuth2实现了很长一段时间。企业也正在朝OAuth2采纳方向发展。</p> <p>我发现OAuth2规范比较简单。然而,如果你想开始更快,可以在这里找到一篇关于OAuth2基础知识的优秀文章,从而深入了解OAUth2理论概念。</p> <p>Spring Security OAuth项目提供了使用Spring开发符合OAuth2标准的实现所需的所有API。官方的Spring安全oauth项目提供了一个实施OAuth2的综合示例。这个帖子的代码示例灵感来自这个例子本身。这篇文章的目的是为了保护我们的REST API,只需使用所需的最低限度的功能。</p> <p>至少你应该知道OAuth2中的四个关键概念:<br />  </p> <h3>OAuth2角色</h3> <p>OAuth2定义了四个角色:</p> <ul> <li><strong><code>resource owner</code>:</strong><br /> 可能是你 能够授予访问受保护资源的实体。当资源所有者是个人时,它被称为最终用户。</li> <li><strong><code>resource server</code>:</strong><br /> 托管受保护资源的服务器,能够使用访问令牌接受和响应受保护的资源请求。</li> <li><strong><code>client</code>:</strong><br /> 代表资源所有者及其授权的应用程序生成受保护的资源请求。它可能是一个移动应用程序,要求您访问您的Facebook订阅源,REST客户端尝试访问REST API,一个网站[Stackoverflow例如]使用Facebook帐户提供备用登录选项。</li> <li><strong><code>authorization server</code>:</strong><br /> 服务器在成功验证资源所有者并获得授权后,向客户端发出访问令牌。</li> </ul> <p>在我们的示例中,我们的REST API只能通过资源服务器进行访问,这将需要存在请求的访问令牌<br />  </p> <h3>2. OAuth2授权授权类型</h3> <p>授权授权是表示资源所有者授权(访问其受保护的资源)的凭据,由客户端使用以获取访问令牌。规范定义了四种授权类型:</p> <ul> <li><code>authorization code</code></li> <li><code>implicit</code></li> <li><code>resource owner password credentials</code></li> <li><code>client credentials</code></li> </ul> <p>我们将使用<code>resource owner password credentials</code>授权类型。原因很简单,我们没有实现将我们重定向到登录页面的视图。仅客户端[Postman或RestTemplate为基础的Java客户端例如]拥有资源所有者的凭据,并且他们将这些凭证以及客户机凭证提供给授权服务器,以便最终接收访问令牌[和可选刷新令牌],然后使用该令牌实际访问资源。</p> <p>一个常见的例子是<code>GMail app</code>您的智能手机上的[客户端],您需要您的凭据并使用它们进行连接<code>GMail servers</code>。它还显示“密码凭证授予”最适合当客户端和服务器与信任在同一个公司时,您不想向第三方提供凭据。</p> <h3>OAuth2令牌</h3> <p>令牌是实现特定的随机字符串,由授权服务器生成,并在客户端请求时发出。</p> <ul> <li><code>Access Token</code> :发送每个请求,通常有效期很短的一段时间[一小时例如]</li> <li><code>Refresh Token</code> :主要用于获取新的访问令牌,不发送每个请求,通常比访问令牌寿命更长。</li> </ul> <strong>HTTPS上的一个词</strong>:对于任何类型的安全实现,从基本身份验证到完整的OAuth2实现<strong><code>HTTPS</code></strong>都是必须的。没有HTTPS,无论您的实现是什么,安全性都将受到威胁。 <h3>OAuth2访问令牌范围</h3> <p>客户端可以使用范围[想要访问此用户的Facebook帐户的Feed和照片]来查询具有特定访问权限的资源,授权服务器又返回显示实际授予客户端访问权限的范围[资源所有者只允许Feed访问,没有照片例如]。</p> <hr />我们进入代码 <p>我们来实现使用Spring Security实现OAuth的必要构建块,以便访问我们的REST资源。</p> <h3>资源服务器</h3> <p>资源服务器托管客户端感兴趣的资源[我们的REST API]。资源位于<code>/user/</code>。<code>@EnableResourceServer</code>注释,应用于OAuth2资源服务器,启用使用传入OAuth2令牌对请求进行身份验证的Spring Security过滤器。类<code>ResourceServerConfigurerAdapter</code>实现<code>ResourceServerConfigurer</code>提供了调整由OAuth2安全保护的访问规则和路径的方法。</p> <pre> <code class="language-java">package com.websystique.springmvc.security;   import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;   @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {       private static final String RESOURCE_ID = "my_rest_api";           @Override     public void configure(ResourceServerSecurityConfigurer resources) {         resources.resourceId(RESOURCE_ID).stateless(false);     }       @Override     public void configure(HttpSecurity http) throws Exception {         http.         anonymous().disable()         .requestMatchers().antMatchers("/user/**")         .and().authorizeRequests()         .antMatchers("/user/**").access("hasRole('ADMIN')")         .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());     }   }</code></pre> <h3><br /> 授权服务器是一个负责验证凭据的人员,如果凭据确定,提供令牌[刷新令牌以及访问令牌]。它还包含有关注册的客户端和可能的访问范围和授权类型的信息。令牌存储用于存储令牌。我们将使用内存中的令牌存储。<code>@EnableAuthorizationServer</code>在当前应用程序上下文中启用授权服务器(即AuthorizationEndpoint和TokenEndpoint)。类<code>AuthorizationServerConfigurerAdapter</code>实现<code>AuthorizationServerConfigurer</code>,它提供了配置授权服务器的所有必要方法。2.授权服务器</h3> <pre> <code class="language-java"> package com.websystique.springmvc.security;   import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.approval.UserApprovalHandler; import org.springframework.security.oauth2.provider.token.TokenStore;   @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {       private static String REALM="MY_OAUTH_REALM";           @Autowired     private TokenStore tokenStore;       @Autowired     private UserApprovalHandler userApprovalHandler;       @Autowired     @Qualifier("authenticationManagerBean")     private AuthenticationManager authenticationManager;       @Override     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {           clients.inMemory()             .withClient("my-trusted-client")             .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")             .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")             .scopes("read", "write", "trust")             .secret("secret")             .accessTokenValiditySeconds(120).//Access token is only valid for 2 minutes.             refreshTokenValiditySeconds(600);//Refresh token is only valid for 10 minutes.     }       @Override     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {         endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)                 .authenticationManager(authenticationManager);     }       @Override     public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {         oauthServer.realm(REALM+"/client");     }   }</code></pre> <p><br /> <br /> 向客户端注册客户端“我的信任客户端”和密码“秘密”以及允许的角色和范围。以上配置</p> <ul> <li>指定任何生成的访问令牌只有120秒有效</li> <li>指定任何生成的刷新令牌只有600秒有效</li> </ul> <h3>3.安全配置</h3> <p>粘在一起 端点<code>/oauth/token</code>用于请求令牌[访问或刷新]。资源所有者[bill,bob]在这里配置。</p> <pre> <code class="language-java">package com.websystique.springmvc.security;   import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.approval.ApprovalStore; import org.springframework.security.oauth2.provider.approval.TokenApprovalStore; import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler; import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;   @Configuration @EnableWebSecurity public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {       @Autowired     private ClientDetailsService clientDetailsService;           @Autowired     public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {         auth.inMemoryAuthentication()         .withUser("bill").password("abc123").roles("ADMIN").and()         .withUser("bob").password("abc123").roles("USER");     }       @Override     protected void configure(HttpSecurity http) throws Exception {         http         .csrf().disable()         .anonymous().disable()         .authorizeRequests()         .antMatchers("/oauth/token").permitAll();     }       @Override     @Bean     public AuthenticationManager authenticationManagerBean() throws Exception {         return super.authenticationManagerBean();     }         @Bean     public TokenStore tokenStore() {         return new InMemoryTokenStore();     }       @Bean     @Autowired     public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){         TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();         handler.setTokenStore(tokenStore);         handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));         handler.setClientDetailsService(clientDetailsService);         return handler;     }           @Bean     @Autowired     public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {         TokenApprovalStore store = new TokenApprovalStore();         store.setTokenStore(tokenStore);         return store;     }       }</code></pre> <p>另外,启用全局方法安全性,如果我们想要使用它,它将激活@PreFilter,@PostFilter,@PreAuthorize @PostAuthorize注释。</p> <pre> <code class="language-java"> package com.websystique.springmvc.security;   import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;   @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {     @Autowired     private OAuth2SecurityConfiguration securityConfig;       @Override     protected MethodSecurityExpressionHandler createExpressionHandler() {         return new OAuth2MethodSecurityExpressionHandler();     } }</code></pre> <h3>4.终点及其目的</h3> <ul> <li>尝试访问资源[REST API],无需任何授权[将当然失败]。<br /> <code>GET http://localhost:8080/SpringSecurityOAuth2Example/user/</code></li> <li>问令牌[接入+刷新]使用<strong>HTTP POST</strong>上<code>/oauth/token</code>,与grant_type =密码和资源所有者凭证REQ-PARAMS。另外,在授权头中发送客户端凭据。 <p> </p> <p><code>POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=password&username=bill&password=abc123</code></p> </li> <li>要求通过有效的刷新令牌新的访问令牌,使用<strong>HTTP POST</strong>上<code>/oauth/token</code>,与grant_type = refresh_token,并发送实际的刷新令牌。另外,在授权头中发送客户端凭据。 <p> </p> <p><code>POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=refresh_token&refresh_token=094b7d23-973f-4cc1-83ad-8ffd43de1845</code></p> </li> <li>通过使用<code>access_token</code>具有请求的查询参数提供访问令牌来访问资源。<br /> <code>GET http://localhost:8080/SpringSecurityOAuth2Example/user/?access_token=3525d0e4-d881-49e7-9f91-bcfd18259109</code></li> </ul> <h3>5.休息API</h3> <p>我在大部分帖子中使用的简单的Spring REST API。</p> <pre> <code class="language-java"> package com.websystique.springmvc.controller;    import java.util.List;    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.util.UriComponentsBuilder;    import com.websystique.springmvc.model.User; import com.websystique.springmvc.service.UserService;    @RestController public class HelloWorldRestController {        @Autowired     UserService userService;  //Service which will do all data retrieval/manipulation work               //-------------------Retrieve All Users--------------------------------------------------------            @RequestMapping(value = "/user/", method = RequestMethod.GET)     public ResponseEntity<List<User>> listAllUsers() {         List<User> users = userService.findAllUsers();         if(users.isEmpty()){             return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND         }         return new ResponseEntity<List<User>>(users, HttpStatus.OK);     }           //-------------------Retrieve Single User--------------------------------------------------------            @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE})     public ResponseEntity<User> getUser(@PathVariable("id") long id) {         System.out.println("Fetching User with id " + id);         User user = userService.findById(id);         if (user == null) {             System.out.println("User with id " + id + " not found");             return new ResponseEntity<User>(HttpStatus.NOT_FOUND);         }         return new ResponseEntity<User>(user, HttpStatus.OK);     }                      //-------------------Create a User--------------------------------------------------------            @RequestMapping(value = "/user/", method = RequestMethod.POST)     public ResponseEntity<Void> createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) {         System.out.println("Creating User " + user.getName());            if (userService.isUserExist(user)) {             System.out.println("A User with name " + user.getName() + " already exist");             return new ResponseEntity<Void>(HttpStatus.CONFLICT);         }            userService.saveUser(user);            HttpHeaders headers = new HttpHeaders();         headers.setLocation(ucBuilder.path("/user/{id}").buildAndExpand(user.getId()).toUri());         return new ResponseEntity<Void>(headers, HttpStatus.CREATED);     }               //------------------- Update a User --------------------------------------------------------            @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)     public ResponseEntity<User> updateUser(@PathVariable("id") long id, @RequestBody User user) {         System.out.println("Updating User " + id);                    User currentUser = userService.findById(id);                    if (currentUser==null) {             System.out.println("User with id " + id + " not found");             return new ResponseEntity<User>(HttpStatus.NOT_FOUND);         }            currentUser.setName(user.getName());         currentUser.setAge(user.getAge());         currentUser.setSalary(user.getSalary());                    userService.updateUser(currentUser);         return new ResponseEntity<User>(currentUser, HttpStatus.OK);     }        //------------------- Delete a User --------------------------------------------------------            @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)     public ResponseEntity<User> deleteUser(@PathVariable("id") long id) {         System.out.println("Fetching & Deleting User with id " + id);            User user = userService.findById(id);         if (user == null) {             System.out.println("Unable to delete. User with id " + id + " not found");             return new ResponseEntity<User>(HttpStatus.NOT_FOUND);         }            userService.deleteUserById(id);         return new ResponseEntity<User>(HttpStatus.NO_CONTENT);     }               //------------------- Delete All Users --------------------------------------------------------            @RequestMapping(value = "/user/", method = RequestMethod.DELETE)     public ResponseEntity<User> deleteAllUsers() {         System.out.println("Deleting All Users");            userService.deleteAllUsers();         return new ResponseEntity<User>(HttpStatus.NO_CONTENT);     }    }</code></pre> <h3><br /> 运行应用程序</h3> <p>运行它并使用两个不同的客户端进行测试。<br /> <br />  </p> <h4>客户端1:Postman</h4> <p>尝试访问一个没有任何信息的资源,将获得一个401。</p> <p><img alt="SpringOAuth2_img1" class="img-thumbnail" src="/resources/assist/images/blog/bbd70c98-9e02-46e9-b4b7-c4bdfadb1d93.png" /></p> <p>让我们得到令牌。首先添加一个<strong>客户端凭证</strong> [my-trusted-client / secret] 的授权头文件。</p> <p><img alt="SpringOAuth2_img2" class="img-thumbnail" src="/resources/assist/images/blog/9c19299c-5b53-4dfb-97f7-30b81963ac1f.png" /></p> <p>单击更新请求,验证标题标签中的标题。</p> <p><img alt="SpringOAuth2_img3" class="img-thumbnail" src="/resources/assist/images/blog/e404605e-f273-476d-a582-0147146e96d9.png" /></p> <p>发送POST请求时,您将收到包含响应<code>access-token</code>以及<code>refresh-token</code>。</p> <p><img alt="SpringOAuth2_img4" class="img-thumbnail" src="/resources/assist/images/blog/a0e2e85e-0a1b-40f8-86e4-3c9f1c062276.png" /></p> <p>将这些令牌保存在某个地方,您将需要它们。现在,您可以使用此访问令牌(有效期为2分钟)来访问资源。</p> <p><img alt="SpringOAuth2_img5" class="img-thumbnail" src="/resources/assist/images/blog/77e1a43f-c866-4b33-b1f0-f1c7d5232f89.png" /></p> <p>2分钟后,access-token将过期,您的进一步资源请求将失败。</p> <p><img alt="SpringOAuth2_img6" class="img-thumbnail" src="/resources/assist/images/blog/dc75aaec-9ce3-4fe0-8bb7-c98e3e8c6054.png" /></p> <p>我们需要一个新的访问令牌。通过刷新令牌来触发一个帖子,以获得全新的访问令牌。</p> <p><img alt="SpringOAuth2_img7" class="img-thumbnail" src="/resources/assist/images/blog/34a5980f-0d49-416a-adf4-fe4182056648.png" /></p> <p>使用这个新的访问令牌访问资源。</p> <p><img alt="SpringOAuth2_img8" class="img-thumbnail" src="/resources/assist/images/blog/4e0b3771-92ed-438a-bc0f-15de7bc6fcf1.png" /></p> <p>刷新令牌也过期[10分钟]。之后,您将看到刷新请求失败。</p> <p><img alt="SpringOAuth2_img9" class="img-thumbnail" src="/resources/assist/images/blog/c8760e84-cdfa-4760-ad3e-0ff07768c117.png" /></p> <p>这意味着您需要请求新的刷新+访问令牌,如步骤2中所示。</p> <h4>客户端2:基于RestTemplate的java应用程序</h4> <p>方法<strong>sendTokenRequest</strong>用于实际获取令牌。然后,我们收到的访问令牌将被用于每个请求。如果需要,您可以在下面的示例中轻松实现刷新令牌流。</p> <pre> <code class="language-java">package com.websystique.springmvc; import java.net.URI; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import org.apache.commons.codec.binary.Base64; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.client.RestTemplate; import com.websystique.springmvc.model.AuthTokenInfo; import com.websystique.springmvc.model.User; public class SpringRestClient { public static final String REST_SERVICE_URI = "http://localhost:8080/SpringSecurityOAuth2Example"; public static final String AUTH_SERVER_URI = "http://localhost:8080/SpringSecurityOAuth2Example/oauth/token"; public static final String QPM_PASSWORD_GRANT = "?grant_type=password&username=bill&password=abc123"; public static final String QPM_ACCESS_TOKEN = "?access_token="; /* * Prepare HTTP Headers. */ private static HttpHeaders getHeaders(){ HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); return headers; } /* * Add HTTP Authorization header, using Basic-Authentication to send client-credentials. */ private static HttpHeaders getHeadersWithClientCredentials(){ String plainClientCredentials="my-trusted-client:secret"; String base64ClientCredentials = new String(Base64.encodeBase64(plainClientCredentials.getBytes())); HttpHeaders headers = getHeaders(); headers.add("Authorization", "Basic " + base64ClientCredentials); return headers; } /* * Send a POST request [on /oauth/token] to get an access-token, which will then be send with each request. */ @SuppressWarnings({ "unchecked"}) private static AuthTokenInfo sendTokenRequest(){ RestTemplate restTemplate = new RestTemplate(); HttpEntity<String> request = new HttpEntity<String>(getHeadersWithClientCredentials()); ResponseEntity<Object> response = restTemplate.exchange(AUTH_SERVER_URI+QPM_PASSWORD_GRANT, HttpMethod.POST, request, Object.class); LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>)response.getBody(); AuthTokenInfo tokenInfo = null; if(map!=null){ tokenInfo = new AuthTokenInfo(); tokenInfo.setAccess_token((String)map.get("access_token")); tokenInfo.setToken_type((String)map.get("token_type")); tokenInfo.setRefresh_token((String)map.get("refresh_token")); tokenInfo.setExpires_in((int)map.get("expires_in")); tokenInfo.setScope((String)map.get("scope")); System.out.println(tokenInfo); //System.out.println("access_token ="+map.get("access_token")+", token_type="+map.get("token_type")+", refresh_token="+map.get("refresh_token") //+", expires_in="+map.get("expires_in")+", scope="+map.get("scope"));; }else{ System.out.println("No user exist----------"); } return tokenInfo; } /* * Send a GET request to get list of all users. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private static void listAllUsers(AuthTokenInfo tokenInfo){ Assert.notNull(tokenInfo, "Authenticate first please......"); System.out.println("\nTesting listAllUsers API-----------"); RestTemplate restTemplate = new RestTemplate(); HttpEntity<String> request = new HttpEntity<String>(getHeaders()); ResponseEntity<List> response = restTemplate.exchange(REST_SERVICE_URI+"/user/"+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(), HttpMethod.GET, request, List.class); List<LinkedHashMap<String, Object>> usersMap = (List<LinkedHashMap<String, Object>>)response.getBody(); if(usersMap!=null){ for(LinkedHashMap<String, Object> map : usersMap){ System.out.println("User : id="+map.get("id")+", Name="+map.get("name")+", Age="+map.get("age")+", Salary="+map.get("salary"));; } }else{ System.out.println("No user exist----------"); } } /* * Send a GET request to get a specific user. */ private static void getUser(AuthTokenInfo tokenInfo){ Assert.notNull(tokenInfo, "Authenticate first please......"); System.out.println("\nTesting getUser API----------"); RestTemplate restTemplate = new RestTemplate(); HttpEntity<String> request = new HttpEntity<String>(getHeaders()); ResponseEntity<User> response = restTemplate.exchange(REST_SERVICE_URI+"/user/1"+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(), HttpMethod.GET, request, User.class); User user = response.getBody(); System.out.println(user); } /* * Send a POST request to create a new user. */ private static void createUser(AuthTokenInfo tokenInfo) { Assert.notNull(tokenInfo, "Authenticate first please......"); System.out.println("\nTesting create User API----------"); RestTemplate restTemplate = new RestTemplate(); User user = new User(0,"Sarah",51,134); HttpEntity<Object> request = new HttpEntity<Object>(user, getHeaders()); URI uri = restTemplate.postForLocation(REST_SERVICE_URI+"/user/"+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(), request, User.class); System.out.println("Location : "+uri.toASCIIString()); } /* * Send a PUT request to update an existing user. */ private static void updateUser(AuthTokenInfo tokenInfo) { Assert.notNull(tokenInfo, "Authenticate first please......"); System.out.println("\nTesting update User API----------"); RestTemplate restTemplate = new RestTemplate(); User user = new User(1,"Tomy",33, 70000); HttpEntity<Object> request = new HttpEntity<Object>(user, getHeaders()); ResponseEntity<User> response = restTemplate.exchange(REST_SERVICE_URI+"/user/1"+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(), HttpMethod.PUT, request, User.class); System.out.println(response.getBody()); } /* * Send a DELETE request to delete a specific user. */ private static void deleteUser(AuthTokenInfo tokenInfo) { Assert.notNull(tokenInfo, "Authenticate first please......"); System.out.println("\nTesting delete User API----------"); RestTemplate restTemplate = new RestTemplate(); HttpEntity<String> request = new HttpEntity<String>(getHeaders()); restTemplate.exchange(REST_SERVICE_URI+"/user/3"+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(), HttpMethod.DELETE, request, User.class); } /* * Send a DELETE request to delete all users. */ private static void deleteAllUsers(AuthTokenInfo tokenInfo) { Assert.notNull(tokenInfo, "Authenticate first please......"); System.out.println("\nTesting all delete Users API----------"); RestTemplate restTemplate = new RestTemplate(); HttpEntity<String> request = new HttpEntity<String>(getHeaders()); restTemplate.exchange(REST_SERVICE_URI+"/user/"+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(), HttpMethod.DELETE, request, User.class); } public static void main(String args[]){ AuthTokenInfo tokenInfo = sendTokenRequest(); listAllUsers(tokenInfo); getUser(tokenInfo); createUser(tokenInfo); listAllUsers(tokenInfo); updateUser(tokenInfo); listAllUsers(tokenInfo); deleteUser(tokenInfo); listAllUsers(tokenInfo); deleteAllUsers(tokenInfo); listAllUsers(tokenInfo); } }</code></pre> <br /> 以上代码将产生以下输出: <pre> <code>AuthTokenInfo [access_token=fceed386-5923-4bf8-b193-1d76f95da4c4, token_type=bearer, refresh_token=29d28ee2-9d09-483f-a2d6-7f93e7a31667, expires_in=71, scope=read write trust] Testing listAllUsers API----------- User : id=1, Name=Sam, Age=30, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=3, Name=Jerome, Age=45, Salary=30000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 Testing getUser API---------- User [id=1, name=Sam, age=30, salary=70000.0] Testing create User API---------- Location : http://localhost:8080/SpringSecurityOAuth2Example/user/5 Testing listAllUsers API----------- User : id=1, Name=Sam, Age=30, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=3, Name=Jerome, Age=45, Salary=30000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 User : id=5, Name=Sarah, Age=51, Salary=134.0 Testing update User API---------- User [id=1, name=Tomy, age=33, salary=70000.0] Testing listAllUsers API----------- User : id=1, Name=Tomy, Age=33, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=3, Name=Jerome, Age=45, Salary=30000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 User : id=5, Name=Sarah, Age=51, Salary=134.0 Testing delete User API---------- Testing listAllUsers API----------- User : id=1, Name=Tomy, Age=33, Salary=70000.0 User : id=2, Name=Tom, Age=40, Salary=50000.0 User : id=4, Name=Silvia, Age=50, Salary=40000.0 User : id=5, Name=Sarah, Age=51, Salary=134.0 Testing all delete Users API---------- Testing listAllUsers API----------- No user exist----------</code></pre>   <h3>项目结构</h3> <img alt="10" class="img-thumbnail" src="/resources/assist/images/blog/aab904e5-276f-4fac-8bd4-3762adb1a28c.png" /> <h3>pom.xml</h3> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.websystique.springmvc</groupId> <artifactId>SpringSecurityOAuth2Example</artifactId> <version>1.0.0</version> <packaging>war</packaging> <name>SpringSecurityOAuth2Example</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <springframework.version>4.3.1.RELEASE</springframework.version> <springsecurity.version>4.1.1.RELEASE</springsecurity.version> <springsecurityoauth2.version>2.0.10.RELEASE</springsecurityoauth2.version> <jackson.library>2.7.5</jackson.library> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springframework.version}</version> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${springsecurity.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${springsecurity.version}</version> </dependency> <!-- Spring Security OAuth2--> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>${springsecurityoauth2.version}</version> </dependency> <!-- Jackson libraries --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.library}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>${jackson.library}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.4</version> <configuration> <warSourceDirectory>src/main/webapp</warSourceDirectory> <warName>SpringSecurityOAuth2Example</warName> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> <finalName>SpringSecurityOAuth2Example</finalName> </build> </project></code></pre> <h4><strong><a href="http://websystique.com/?smd_process_download=1&download_id=2926" rel="external nofollow" target="_blank"><em><u>下载源代码</u></em></a></strong></h4> <br />