[思路分享]从零开始搭建并部署jsp+servlet+bootstrap+mysql博客

[思路分享]从零开始搭建并部署jsp+servlet+bootstrap+mysql博客

[TOC]

本文主要写一下思路和一些小知识点

前段时间学了下web开发相关的知识,尝试实现了个博客后顺便写个总结

数据库

记得把connector包放到Tomcat/lib目录下,否则会报错哦

另外要把connector放到WEB INF的lib下

Tomcat版本>10,则java import的时候跟servlet,http相关的都要从jakarta引用(来源为tomcat)而不是java或者其它的

user表

表名 User 主键 Id
列名 数据类型 是否允许为空 描述
Id int(50) No 用户id
username varchar(50) No 用户名
password varchar(50) No 密码

article表

表名 Article 主键 Id
列名 数据类型 是否允许为空 描述
Id int(50) No 文章id
title text No 文章标题
subtitle text 文章摘要
md_content longtext 文章md字符串
html_content longtext No 文章html字符串
createdate varchar(500) No 发表日期
category varchar(500) 分类
top int(10) 置顶标记

实体类User.java和Article.java和它们对应即可

记得写好包含操纵数据库静态方法的JdbcUtil类

还有通过传入的实体类对象 针对user,article表增删改查的UserDao,和ArticleDAO,然后有UserService.java和ArticleService根据逻辑服务,操控DAO

写代码的时候记得防范数据库注入攻击,尤其对sql语句关键字做一些识别和屏蔽

这里给个JdbcUtil的例子,网上随便抄的..但是原网站网址失效了就不放网址了


import java.io.InputStream;
import com.mysql.jdbc.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.*;
import java.util.*;




public class JdbcUtil {
    private static int flag=0;
    // 表示定义数据库的用户名
    private static String USERNAME;
    // 定义数据库的密码
    private static String PASSWORD;
    // 定义数据库的驱动信息
    private static String DRIVER;
    // 定义访问数据库的地址
    private static String URL;
    static {
        //加载数据库配置信息,并给相关的属性赋值
        loadConfig();
    }
    // 定义数据库的链接
    private Connection connection;
    // 定义sql语句的执行对象
    private PreparedStatement pstmt;
    // 定义查询返回的结果集合
    private ResultSet resultSet;
    private static JdbcUtil singleDbUtil = new JdbcUtil();

    private JdbcUtil() {

    }

    public static  JdbcUtil getInstance()
    {
        return singleDbUtil;
    }
    /**
     * 加载数据库配置信息,并给相关的属性赋值
     */
    public static void loadConfig() {
        try {
            InputStream inStream = JdbcUtil.class.getResourceAsStream("/jdbc.properties");
            Properties prop = new Properties();
            prop.load(inStream);
            USERNAME = prop.getProperty("jdbc.username");
            PASSWORD = prop.getProperty("jdbc.password");
            DRIVER = prop.getProperty("jdbc.driver");
            URL = prop.getProperty("jdbc.url");
            flag=1;
        } catch (Exception e) {
            throw new RuntimeException("读取数据库配置文件异常!", e);
        }
    }

    /**
     * 获取数据库连接
     * @return 数据库连接
     */
    public Connection getConnection() {
        if(flag==0)
        {
            loadConfig();
        }
        try {
            Class.forName(DRIVER); // 注册驱动
            connection = DriverManager.getConnection(URL, USERNAME, PASSWORD); // 获取连接


        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
    /**
     * 执行更新操作
     * @param sql    sql语句
     * @param params 执行参数
     * @return 执行结果
     * @throws SQLException
     */
    public boolean updateByPreparedStatement(String sql, List<?> params)
            throws SQLException {
        boolean flag = false;
        int result = -1;// 表示当用户执行添加删除和修改的时候所影响数据库的行数
        pstmt = connection.prepareStatement(sql);
        int index = 1;
        // 填充sql语句中的占位符
        if (params != null && !params.isEmpty()) {
            for (int i = 0; i < params.size(); i++) {
                pstmt.setObject(index++, params.get(i));
            }
        }
        result = pstmt.executeUpdate();
        flag = result > 0 ? true : false;
        return flag;
    }
    /**
     * 执行查询操作
     * @param sql    sql语句
     * @param params 执行参数
     * @return
     * @throws SQLException
     */
    public List<Map<String, Object>> findResult(String sql, List<?> params)
            throws SQLException {
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        int index = 1;
        pstmt = connection.prepareStatement(sql);
        if (params != null && !params.isEmpty()) {
            for (int i = 0; i < params.size(); i++) {
                pstmt.setObject(index++, params.get(i));
            }
        }
        resultSet = pstmt.executeQuery();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int cols_len = metaData.getColumnCount();
        while (resultSet.next()) {
            Map<String, Object> map = new HashMap<String, Object>();
            for (int i = 0; i < cols_len; i++) {
                String cols_name = metaData.getColumnName(i + 1);
                Object cols_value = resultSet.getObject(cols_name);
                if (cols_value == null) {
                    cols_value = "";
                }
                map.put(cols_name, cols_value);
            }
            list.add(map);
        }
        return list;
    }
    /**
     * 释放资源
     */
    public void releaseConn() {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        JdbcUtil jdbcUtil = new JdbcUtil();
        jdbcUtil.getConnection();
        try {
            List<Map<String, Object>> result = jdbcUtil.findResult(
                    "select * from article", null);
            for (Map<String, Object> m : result) {
                System.out.println(m);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            jdbcUtil.releaseConn();
        }
    }
}

登入&登出&访问权限控制

思路

由于是个人博客,不需要给别人登入的选项

使用http post 方法+servlet读取帐号密码,以及验证码

在login.jsp中,用一个post表单把帐号密码发给servlet

servlet里,在数据库查询帐号,然后check一下帐号对不对,对的话在session

中set一个flag (session可以简单理解成每个客户端的专属map),代表登入成功

接着,对于管理页(删文章,添文章等),设立一个 filter,在filter中get session的flog,如果成功则使用

知识点学习

bootstrap使用

简单来说就像一个库,类比opencv,导入到项目中,html标签中写一下 class = .xxx就直接使用bootstrap的样式3

https://v3.bootcss.com/getting-started/#download 官网

这个bootStrap必须依赖jquery.min.js的存在,必须。

所以一并下载这个js 网址:http://www.jq22.com/jquery-info122

把外部文件引入工程里面

即把bootStrap官网下载下来的目录里的css、font、js、下载的jquery.min.js一并复制到项目下面

比如我的idea创建web工程后 index.jsp文件在项目目录/web下,那我就把css,font,js放到web下(不这样也可以,在jsp页面导入的时候地址改一下也可以,但是这样的话可以直接到官方复制页面用,不用改)

第三步、jsp页面设置如下

注意这几个文件的前后顺序,如果不对,会导致页面无法正常运行。

先引入 bootstrap.min.css (Bootstrap的样式表文件)
然后引入自己写的 css 文件(style.css)
然后引入 jQuery(javascript 库)
最后引入 bootstrap.min.js 程序文件

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!--这个lang="zh-CN"是转化为html5的版本  -->
<html lang="zh-CN">
<head>
<base href="<%=basePath%>">
<title>学习bootstrap案例</title>
<!-- 这个是自适应各种分辨率的屏幕 -->
<meta name="viewport" content="width=device-width, initial-scale=1">

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<link rel="stylesheet"  href="css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/style.css"/>
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>

</head>

后面的部分(body)就可以直接到官网找个例子,然后按F12看源码,把<body>部分的源码复制下来用,另外,自己写完打开网页测试时按f12有报错404,基本上是脚本,路径等的小问题,删了或者下相应脚本即可

这部分参考了https://blog.csdn.net/qq_37591637/article/details/84024560

session

session对象
什么是session:
session是从客户端访问服务器开始,到关闭浏览器的一个过程,是一个时间概念,也是一个过程的概念。
session用于区分不同的客户端,保存在服务端的内存中,和用户一一对应。

常用方法:

<body>
    session的创建时间:<%
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
            Date d = new Date(session.getCreationTime());
            out.println(sdf.format(d)); //毫秒变成规范格式 
            %> <br>
    获取当前用户的唯一的SessionId:<%=session.getId()%> <br>
    从Session中获取用户名:<%
                     session.setAttribute("username","nc"); //设置当前用户的属性(key,value)
                     out.println(session.getAttribute("username"));  //根据键值获取值
                     %><br>
    获取当前所有可用属性:<%
                    session.setAttribute("password","123123");
                    session.setAttribute("sex","man");
                    String[] attrs = session.getValueNames();
                    for(String attr:attrs)
                    {
                        out.print(attr+"&nbsp;");
                    }
                  %><br>
    当前session的最大生命周期:<%=session.getMaxInactiveInterval() %>  <!-- 默认为半小时 -->
    <%
        session.setMaxInactiveInterval(10); //设置session的最大生命周期(单位秒),过期后会创建新的session
     %>
  </body>

session的生命周期:

  1. 创建:当浏览器第一次访问服务器时服务器就会创建一个session对象,并且为它设立唯一的SessionID,保存于服务器端,以后每次浏览器访问服务器时都会检查该SessionID是否相同,如果不同则会重新创建一个session.
  2. 活动:当已经创建了session之后在没有关闭浏览器等清除session的情况下,依然可以访问服务器的一系列动作。分别有三种情况:
    —–在一个session中通过超链接访问一个页面可以认为是属于同一个会话;
    —–在没有全部关闭所有的页面的情况下,重新打开新的浏览器窗口访问该网站,属于同一个会话;
    —–当全部关闭所有的页面的情况下,再访问服务器时属于不同的会话,重新建立session.(前两种指的是在同一个浏览器中)
    注意:当关闭了窗口后,重新建立了session,但是原来的session依然存在于服务器端,只是没有客户端携带sessionID去访问而已
  3. 销毁:销毁有三种方式:
    —–重启服务器;
    —–调用invalidate()方法,直接清除 session.invalidate();
    —–设置过期时间,过期自动销毁 session.setMaxInactiveInterval();
    或者设置web.xml文件
<session-config>
    <session-timeout> 1  </session-timeout>  <!--分钟-->
 </session-config>

application对象
application对象是javax.servlet.ServletContext的实例
生命周期是从服务器启动,创建application对象直到服务器关闭,服务器的启动和关闭决定了application对象的生命
实现了用户间数据的共享,可存放全局变量
在用户的前后连接或不同用户之间的连接中,可以对application对象的同一属性进行操作

常用方法:
public void setAttribute(String name,Object value) 使用指定名称将对象绑定到此会话
public Object getAttribute(String name) 返回于此会话中指定名称绑定在一起的对象,如果没有对象绑定在该名称下,则返回null。
Enumeration getAttributeName() 返回所有可用属性名的枚举
String getServerInfo() 返回jsp(servlet)引擎名及版本号

//eg:网站计数器
<body>
  <%
    Integer count = (Integer)application.getAttribute("count");
    if(count==null){
        count = 1;
    }
    else{
        count++;
    }
    application.setAttribute("count",count);
   %>
    欢迎访问本网站,您是第<%=count %>位访问的用户!
</body>
out对象
out对象是JspWriter类的实例,是向客户端输出内容常用的对象,它包含很多IO流中的方法和特性

常用方法:

void println() //向客户端打印字符串
void clear() //清除缓冲区的内容,如果在flush之后调用会抛出异常,导致后面的代码不会执行
void clearBuffer() //清除缓冲区的内容,如果在flush之后调用不会抛出异常
void flush() //将缓冲区内容输出到客户端
int getBufferSize() //返回缓冲区以字节数的大小,如不设缓冲区则为0
int getRemaining() //返回缓冲区还剩余多少可用
Boolean isAutoFlush() //返回缓冲区满时,是自动清空还是抛出异常
void close() //关闭输出流

html表单

一、表单的认识

1、 什么是表单?

表单在网页中主要负责“数据采集”功能。

一个表单有三个基本组成部分:

表单标签:这里面包含了处理表单数据请求URL地址以及数据提交到服务器的方式。

表单域:包含了文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等。

表单按钮:包括提交按钮、复位按钮和一般按钮,用于将数据传送到服务器上的处理程序或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作。

2、表单的应用

表单的常见应用:搜索、登录、注册、完善信息、评论框、发表文章、问卷调查

二、 表单的三大结构和属性

1、表单的三大结构

< form name=”表单名称” action=”表单处理程序页面” method=”post/get”>

< fieldset>

< legend>表单名称 < /legend>

< input type=”控件类型” name=”控件名称”>

< input type=”submit” value=”提交”>

< /fieldset>

< /form>

2、表单的基本语法和属性

< form action=”URL” method=”get|post”>…< /form>

属性说明:

[accept-charset]属性,值为charset_list,是规定服务器可处理的表单数据字符集。

[action]属性,值为URL,是规定当提交表单时向何处发送表单数据。

[autocomplete]属性,值为onoff,是规定是否启用表单的自动完成功能。

[enctpe]属性,可能的值有application/text/plain,是规定在发送表单数据之前如何对其进行编码。上传文件时必须指定为:multipart/form-data

[method]属性,值为get(默认)post,是规定用于发送 form-data 的 HTTP方法。

[name]属性,值为form_name是规定表单的名称。

[novalidate]属性,值为novalidate,如果使用该属性,则提交表单时不进行验证。

[target]属性,值为 _blank_self(默认)_parent_topframename,是规定在何处打开 action URL。

三、表单元素

1、 输入框input (重点)

< input /> 标签用于收集用户信息。

根据不同的 type 属性值,输入字段拥有很多种形式。输入字段可以是文本字段、复选框、掩码后的文本控件、单选按钮、按钮等等。

< input />是一个行内元素,也是一个单标签,需要自关闭。

input类型:

text类型,普通文本框,特点是默认;

password类型,密码框,特点是内容显示为*号;

submit类型,提交按钮,特点是点击后就提交表单;

button类型,普通按钮;

reset类型,重置按钮,特点是回到最初状态(注意:不是清空);

radio类型,单选,特点是一组单选必需name相同;

checkbox类型,多选,特点是一组多选必需name相同;

file类型,文件上传框,特点是可以选择文件进行提交;

hidden类型,隐藏域,特点是隐藏控件,但是会被提交。

file类型的注意事项

默认情况下,enctype的值是application,不能用于文件上传,只有使用了multipart,才能完整的传递文件数据。

application不是不能上传文件,是只能上传文本格式的文件,multipart是将文件以二进制的形式上传,这样可以实现多种类型的文件上传。

2、select

select 元素可创建单选或多选菜单。<seclet>和<option>一般同时使用,select代表的下拉框,option表示它的每一项。

当option中有value的时候,传将value传到后台,没有value的时候,将它显示的内容传到后台

< select name=”country” >

< option value=””>—-请选择—-< /option>

< option value=”zh” selected=”selected”>中国< /option>

< option value=”en”>英国—-< /option>< /select>

select 的属性:

[autofocus]属性,值为autofocus,是规定在页面加载后文本区域自动获得焦点。

[disabled]属性,值为disabled,是规定禁用该下拉列表。

[multiple]属性,值为multiple,是规定可选择多个选项。

[name]属性,值为name,是规定下拉列表的名称。

[size]属性,值为number,是规定下拉列表中可见选项的数目。

option的属性:

[disabled]属性,值为disabled,是规定此选项应在首次加载时被禁用。

[selected]属性,值为selected,是规定选项(在首次显示在列表中时)表现为选中状态。

[value]属性,值为text,是定义送往服务器的选项值。

3、textarea

在网页中,用户需要输入大量的文字信息的时候,就会用到textarea标签,它就是多行文本域。

比如:留言,备注等。

文本区中可容纳无限数量的文本,其中的文本的默认字体是等宽字体(通常是 Courier)。

可以通过 cols 和 rows 属性来规定 textarea 的尺寸,不过更好的办法是使用 CSS 里的height 和 width 属性:cols和rows。

4、button

在 button 元素内部,您可以放置内容,比如文本或图像。这是该元素与使用 input 元素创建的按钮之间的不同之处。

< button> 控件 与 < input type=”button”> 相比,提供了更为强大的功能和更丰富的内容。 与标签之间的所有内容都是按钮的内容,其中包括任何可接受的正文内容,比如文本或多媒体内容。例如,我们可以在按钮中包括一个图像和相关的文本,用它们在按钮中创建一个吸引人的标记图像。

请始终为< button>规定 type 属性。Internet Explorer(低版本) 的默认类型是 “button”,而其他浏览器中(包括 W3C 规范)的默认值是 “submit”。

< button type=”submit” name=”btn-search” id=”btn-search”>

< img src=”url”>< /button>

重要事项:如果在 HTML 表单中使用 button 元素,不同的浏览器会提交不同的值。Internet Explorer 将提交按钮之间的文本,而其他浏览器将提交 value 属性的内容。建议在 HTML 表单中使用 input 元素来创建按钮。

四、表单的语义化

label标签的语义化:

< label>< input type="checkbox">同意协议< /label>< input type="checkbox" id="agree">< label for="agree">同意协议< /label>

fieldset标签和legend标签

< form name="login" action="" method="post">   
    < fieldset>       
        < legend>用户登录</legend>     
        < div class="in">            
            < input type="email" name="email" id="email" placeholder="输入邮箱地址" autocomplete="off" autofocus>       
            < /div>      
                < div class="in">          
                    < input type="submit" value="登录">      

                 < /div>   
      < /fieldset>
   < /form>

fieldset元素用于对表单中的元素进行分组并在文档中区别标出文本。 一个表单可以有多个fieldset标签。

legend元素必位于fieldset内的第一个元素,用于描述表单的内容。

好处:

1)增强表单的语义。

2)可以定义fieldset元素的disabled属性来禁用整个组中的表单元素。

get方法后台读值和post方法后台读值

例子

registe.html

<h4>Servlet GET和POST请求</h4>
<form action="registe" method="post">
        账号:<input type="text" name="username"/><br/>
        密码: <input type="password" name="password"/><br/>
        确认密码:<input type="password" name="password"/><br/>
        性别:<input type="radio" name="sex" value="1"/>男
              <input type="radio" name="sex" value="0"/>女<br/>
        爱好:<input type="checkbox" name="like" value="sport">运动
              <input type="checkbox" name="like" value="food">美食
              <input type="checkbox" name="like" value="shopping">购物<br/>
        居住地:<select name="home">
                    <option>西湖</option>
                    <option>江干</option>
                    <option>上城</option>
                    <option>下城</option>
                </select> <br/>
        <input type="submit" value="注册"/>
</form>

RegisteServlet.java

public class RegisteServlet extends HttpServlet{
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1.获得请求的参数(客户端提交的数据)
        //1)单个获取请求的参数
        String username = req.getParameter("username");

        //获得参数名称相同的多个参数的值(比如复选框等)
        String[] pwds = req.getParameterValues("password");
        System.out.println("密码如下 :");
        for(String pwd:pwds){
            System.out.println(pwd);
        }
        //checkbox复选框        
        String[] likes = req.getParameterValues("like");
        System.out.println("喜好如下:");
        for(String like:likes){
            System.out.println(like);
        }

        //获得下拉列表或单选按钮
        String sex = req.getParameter("sex");
        System.out.println("性别:"+sex);

        //获得单选按钮
        String home = req.getParameter("home");
        System.out.println("居住地:"+home);

        //2)一次获得所有的请求参数
        //返回的是map对象,key-请求的参数名称,value-请求的参数值
        Map<String, String[]> params = req.getParameterMap();
        Set<Entry<String, String[]>> entrys = params.entrySet();

    //2.响应
        resp.setContentType("text/html"); //响应的文件是文本形式的html文件    
        PrintWriter printWriter = resp.getWriter(); //获得输出流对象
        printWriter.print("<!DOCTYPE><html><head><title>user login</title></head>");
        printWriter.print("<body>");
        if("admin".equals(username)&& "admin123".equals(pwds[0])){
            printWriter.print("welcome "+username);
        }else{
            printWriter.print("<font color='red'>username or password is wrong</font>");
        }
        printWriter.print("</body></html>");

        printWriter.flush();
        printWriter.close();
    }
}

get与post提交方式的区别:

get方式的提交

     (1)get提交中请求参数以 ?username=admin&password=admin123 形式拼接在路径(url)的后面

     (2)不安全的提交方式

     (3)提交的参数个数是有限制的,get方式能提交的数据只能是文本,且大小不超过1024个字节

     (4)提交后调用的是doGet()方法处理






post方式的提交

     (1)提交的请求参数是放在请求体中(http包的包体中)

     (2)相对安全的提交方式

     (3)参数个数没有限制的,post不仅可以提交文本还有二进制文件。

     (4)提交后调用的是doPost()方法处理

除了用form表单提交指定为post方式则为post方式提交,其它均为get方式提交

参考自https://blog.csdn.net/qq_42402854/article/details/85014571

一些常用方法

    System.out.println(req.getRequestURI());
    System.out.println(req.getMethod());
    System.out.println(req.getServletPath());
    System.out.println(req.getContextPath());
    System.out.println(req.getScheme());
    System.out.println(req.getServerName());
    System.out.println(req.getServerPort());

验证码案例

前台页面

  <body>
  <form action="<%=request.getContextPath() %>/servlet/LoginServlet" method="get">
      验证码:<input type="text" name="VerifyCode" size=6 />
       <img onclick="reloadCode()" src="<%=request.getContextPath() %>/servlet/ImgServlet" id="img" /> <br>
       <input type="submit" name="submit" value="提交" /> 
  </form>   
        <script>
        function reloadCode(){
            document.getElementById("img").src="<%=request.getContextPath() %>/servlet/ImgServlet?"+Math.random();;
        }
        </script>
  </body>

后台sevlet

package servlet;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ImgServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        BufferedImage bfi = new BufferedImage(68, 22, BufferedImage.TYPE_INT_RGB); //图像缓冲区
        Graphics g = bfi.getGraphics();
        StringBuffer sb = new StringBuffer();
        /*画背景框*/
        Color color = new Color(200,215,250); 
        g.setColor(color);
        g.fillRect(0, 0, 68,30); 
        /*第一个数字*/
        Random r = new Random();
        int tmp1 = r.nextInt(20);
        g.setColor(new Color(r.nextInt(100),r.nextInt(100),r.nextInt(100))); //设置随机颜色
        g.drawString(tmp1+"",3, 18);
        /*加号*/
        g.setColor(new Color(r.nextInt(100),r.nextInt(100),r.nextInt(100))); //设置随机颜色
        g.drawString("+",18, 18);   
        /*第二个数字*/
        int tmp2 = r.nextInt(20);
        g.setColor(new Color(r.nextInt(100),r.nextInt(100),r.nextInt(100))); //设置随机颜色
        g.drawString(tmp2+"",33, 18);   
        /*等号*/
        g.setColor(new Color(r.nextInt(100),r.nextInt(100),r.nextInt(100))); //设置随机颜色
        g.drawString("=",48, 18);   
        /*问号*/
        g.setColor(new Color(r.nextInt(100),r.nextInt(100),r.nextInt(100))); //设置随机颜色
        g.drawString("?",57, 18);
        /*保存到session*/
        int result = tmp1+tmp2;
        request.getSession().setAttribute("VerifyCode", result+"");
        /*写入response输出流*/
        ImageIO.write(bfi, "JPG", response.getOutputStream());
    }
}

登入和权限管理

放个例子

转载 https://blog.csdn.net/icarus_wang/article/details/51422364

img

我们先来编写hello.jsp文件,即用户想要访问的目标文件,

只需让其显示简单的信息即可

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    out.println("Hello World");
%>
</body>
</html>

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆页面</title>
</head>
<style type="text/css">
    body{
        color: #000;
        font-size: 14px;
        margin: 20px auto;
    }
</style>
<script type="text/javascript">
    function check(form) {
        //验证用户名是否为空
        if (document.forms.loginform.uname.value==""){
            alert("请输入用户名");
            //将焦点置于用户名输入框
            document.forms.loginform.uname.focus();
            return false;
        }
        //验证密码是否为空
        if (document.forms.loginform.upwd.value==""){
            alert("请输入用户名");
            //将焦点置于用户名输入框
            document.forms.loginform.upwd.focus();
            return false;
        }
    }
</script>
<body>
    <form action="<%=request.getContextPath()%>/LoginServlet17" method="post" name="loginform">
        <%if (request.getAttribute("return_uri")!=null){%>
        <input type="hidden" name="return_uri" value="<%=request.getAttribute("return_uri")%>"/>
        <%}%>
        <table border="1" cellspacing="0" cellpadding="5" bordercolor="silver" align="center">
            <tr>
                <td colspan="2" align="center" bgcolor="#E8E8E8">用户登录</td>
            </tr>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="uname"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="upwd"></td>
            </tr>
            <tr>
                <td colspan="2" align="center">
                    <input type="submit" name="submit" οnclick="return check(this);">
                    <input type="reset" name="reset">
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
首页
<br/>
<br/>
<a href="<%=request.getContextPath()%>/17/hello.jsp">hello.jsp</a>
<br/>
<%
    String flag="";
    Object object=session.getAttribute("flag");
    if (object!=null){
        flag=object.toString();
    }
    if (flag.equals("login_success")){
%>
<a href="<%=request.getContextPath()%>/LogoutServlet17">退出</a>
<%}else{%>
<a href="<%=request.getContextPath()%>/17/login.jsp">登录</a>
<%}%>
</body>
</html>

我们处理完了表现层的代码编写,那么我们就来编写下逻辑层的Servlet程序。

首先我们需要一个来校验用户名和密码是否正确的登录逻辑处理功能,

LoginServlet17

public class LoginServlet17 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uname=request.getParameter("uname");
        String upwd=request.getParameter("upwd");
        //用户访问登录页面之前所访问的页面,
        // 通过这个值可以在用户登录成功之后跳转到登录之前的界面
        String returnUri=request.getParameter("return_uri");

        System.out.println("用户名:==》"+uname);
        System.out.println("密  码:==》"+upwd);
        System.out.println("Return Uri:==》"+returnUri);

        //判断用户名和密码是否为空
        //用户名和密码都不为空则执行登录逻辑
        RequestDispatcher rd=null;
        if (uname==null||upwd==null){
            request.setAttribute("msg","用户名或密码为空!!!");
            rd=request.getRequestDispatcher("/17/login.jsp");
            rd.forward(request,response);
        }else{
            if (uname.equals("王鑫")&&upwd.equals("123456")){
                //如果登录成功,则在用户的session对象中保存一个kvflag,值为login_success
                request.getSession().setAttribute("flag","login_success");
                //假如用户登录前界面不为空,则跳转到用户登录前界面
                //若用户登录前界面为空,则跳转到首页
                if (returnUri!=null){
                    rd=request.getRequestDispatcher(returnUri);
                    rd.forward(request,response);
                }else{
                    rd=request.getRequestDispatcher("/17/index.jsp");
                    rd.forward(request,response);
                }
            }else{
                request.getSession().setAttribute("flag","login_error");
                request.setAttribute("msg","用户名或密码输入错误!!!");
                rd=request.getRequestDispatcher("/17/login.jsp");
                rd.forward(request,response);

            }

        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

在这里我们获取了用户在登录界面之前所访问的界面,因为在登录成功之后是要将页面重新跳转到用户登录之前的界面的。这个值我们会在过滤器中设置。

除了登录的处理之外,我们还需对登出进行处理,因为我们获取到了用户登录成功后的Session对象信息,所以我们得在登出程序中将Session对象的信息清除掉。并将界面跳转到首页中去。
登出程序LogoutServlet

public class LogoutServlet17 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //直接删除session对象
        request.getSession().invalidate();
        //重定向到首页
        response.sendRedirect(request.getContextPath()+"/17/index.jsp");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

首先我们得明确过滤器所需实现的功能:
1.我们得明确用户想要访问的界面是什么?
2.当确定用户所要访问的界面是需要登录权限的那个界面,则需要判断用户目前的登录状态。
3.当用户未登录或者登录发生错误的话则需将界面跳转到登录界面。

好了,在明确了过滤器所需完成的功能后便可以来实现了。

public class PermissionFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {

    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //首先将参数中的ServletRequest和ServletResponse强转为Http...
        HttpServletRequest hreq= (HttpServletRequest) req;
        HttpServletResponse hresp= (HttpServletResponse) resp;

        //获取请求中的Servletpath
        String servletpath=hreq.getServletPath();
        //获取session对象
        HttpSession hsess=hreq.getSession();
        //获取session对象中flag的值,强转为String类型
        String flag= (String) hsess.getAttribute("flag");

        //如果用户登录的是首页或者是login或者是执行登录操作的话
        // 将请求直接转发给下一个组件进行处理
        //对于其他请求则都进行权限校验
        if (servletpath!=null&&(servletpath.equals("/17/index.jsp"))||
                (servletpath.equals("/17/login.jsp"))||
                (servletpath.equals("/LoginServlet17"))){

            chain.doFilter(req, resp);
        }else{
            //用户处于登录状态则可以直接进行访问
            if (flag!=null&&flag.equals("login_success")){
                chain.doFilter(req, resp);
                //登录失败,则跳转到登录界面
            }else if (flag!=null&&flag.equals("login_error")){
                hreq.setAttribute("msg","登录失败,请重新登录!!!<br/>");
                hreq.setAttribute("return_uri",servletpath);
                RequestDispatcher rd=hreq.getRequestDispatcher("/17/login.jsp");
                rd.forward(hreq,hresp);
                //没有登录则也跳转到login.jsp界面,并提示“您尚未登录!!!”
            }else {
                hreq.setAttribute("msg","您尚未登录!!!");
                hreq.setAttribute("return_uri",servletpath);
                RequestDispatcher rd=hreq.getRequestDispatcher("/17/login.jsp");
                rd.forward(hreq,hresp);
            }
        }
    }

    public void destroy() {
    }

}

在这里我们得明确servletrequest和httpservletrequest 区别?

HttpServletRequest继承ServletRequest,Servlet里有doGet、doPost方法 ,没有doPut方法,是和form的get post对应的.
servletRequest是接口,httpServletRequest是实现,但是有些方法是httpServletRequest独有的,比如getSession().。
HttpServletRequest接口是继承自ServletRequest接口的。增加了和HTTP相关的一些方法。
而所谓的request(在JSP中使用的)其实只是规范中的一个名称而已。它当然是一个对象,但并不是SUN提供的,这是由各个不同的Servlet提供商编写的,SUN只是规定这个类要实现HttpServletRequest接口,并且规定了各个方法的用途,但具体是什么类是由各个提供商自己决定的。

web.xml

<servlet>
    <servlet-name>LoginServlet17</servlet-name>
    <servlet-class>com.icarus.servlet.LoginServlet17</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet17</servlet-name>
    <url-pattern>/LoginServlet17</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>LogoutServlet17</servlet-name>
    <servlet-class>com.icarus.servlet.LogoutServlet17</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LogoutServlet17</servlet-name>
    <url-pattern>/LogoutServlet17</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>PermissionFilter</filter-name>
    <filter-class>com.icarus.filter.PermissionFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>PermissionFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>

markdone解析

使用(editormd)[https://github.com/pandao/editor.md]

https://github.com/pandao/editor.md

官方下载源码后,将其下css,fonts,images,lib,plugins放(or合并)到项目中web下

编辑页面

思路

通过修改example中的simple.html来完成附加功能。

可以如下修改simple.html:

<div id="my-editormd">
            <textarea class="editormd-markdown-textarea" name="doc"
                      style="display:none;"></textarea>
            <!-- 第二个隐藏文本域,用来构造生成的HTML代码,方便表单POST提交,这里的name可以任意取,后台接受时以这个name键为准 -->
            <textarea class="editormd-html-textarea" name="html"></textarea>
</div>


<script type="text/javascript">
    var testEditor;

    $(function () {
        testEditor = editormd("my-editormd", {
            //这边就是把id=my-editormd的html标记部分解析,下面是一些解析选项
            width: "90%",
            height: 640,
            syncScrolling: "single",
            path: "../lib/",
           //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。
            saveHTMLToTextarea: true,
//----图片传输部分
            imageUpload: true,
            imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
            imageUploadURL : "/uploadImage",//注意你后端的上传图片服务地址
            onload: function(){
                this.width("100%");
                this.height(480);
            }
        });
    });
</script>

此后在script里写

var content = $('.editormd-markdown-textarea').val(); 获取值

可以获得markdone 内原始字符串

获取html版本的字符串把命令里的”markdown”换成”html”,这个要用的,服务端编辑jsp把html版本的字符串直接放到editormd函数内标识的div里(上面代码就是id=”my-editormd”的 div),就能对外展示解析后的md文本,用户则是直接再textarea打字就能生成解析md文本

上面的部分放到表单里,然后加一点点细节,前端改一改

用post提交就可以

后台servlet 把文章数据放入数据库就完了

文章页面

思路

mdeditor官网README里有一个例子,把原始md字符串转化成html在页面里显示

https://github.com/pandao/editor.md

<link rel="stylesheet" href="editormd/css/editormd.preview.css" />
<div id="test-markdown-view">
    <!-- Server-side output Markdown text -->
    <textarea style="display:none;">### Hello world!</textarea>             
</div>
<script src="jquery.min.js"></script>
<script src="editormd/editormd.js"></script>
<script src="editormd/lib/marked.min.js"></script>
<script src="editormd/lib/prettify.min.js"></script>
<script type="text/javascript">
    $(function() {
        var testView = editormd.markdownToHTML("test-markdown-view", {
            // markdown : "[TOC]\n### Hello world!\n## Heading 2", // Also, you can dynamic set Markdown text
            // htmlDecode : true,  // Enable / disable HTML tag encode.
            // htmlDecode : "style,script,iframe",  // Note: If enabled, you should filter some dangerous HTML tags for website security.
        });
    });
</script>

以上面官网例程为例,把需要显示的md字符串放到textarea 后面,

将在id=” test-markdown-view”的div块里显示解析后的md

那么文章页面只需要布局好,就好了,限制只能通过servlet跳转到文章,在一个BlogServlet.java里专门处理主要和博客内容相关的请求,如果是访问文章的请求,就在request里加一个attribute,放的是文章Article实体类对象,页面内插入java脚本将内容get出来放到页面里即可

例子,取自BlogServlet的一部分

 if(request.getParameter("action")!=null) {
            if(request.getParameter("action").equals("readArticle"))
            {
                Article currentArticle = result.getDataList().get(0);
                request.setAttribute("currentArticle", currentArticle);
                RequestDispatcher rd = request.getRequestDispatcher("/article.jsp");       //定向的页面

                rd.forward(request, response);
            }

例子,取自article.jsp的一部分

<div id="test-markdown-view" class="card  shadow border-0 p-3" style="background-color: #e0e0e0 ;opacity: 0.95">
    <!-- Server-side output Markdown text -->
    <%=currentArticle.getHtml_content()%>


</div>

    <script type="text/javascript">
    $(function() {
        var testView = editormd.markdownToHTML("test-markdown-view", {
            // markdown : "fucku", // Also, you can dynamic set Markdown text
            htmlDecode : true,  // Enable / disable HTML tag encode.
            // htmlDecode : "style,script,iframe",  // Note: If enabled, you should filter some dangerous HTML tags for website security.
            width   : "100%",
            height  : 700,
            syncScrolling : "single",
            path    : "<%=context%>/admin/lib/editormd/lib",
            saveHTMLToTextarea: true,
            emoji: false,
            taskList: true,
            tocm: true,         // Using [TOCM]
            tex: true,                   // 开启科学公式TeX语言支持,默认关闭
            //flowChart: true,             // 开启流程图支持,默认关闭
            //sequenceDiagram: true,       // 开启时序/序列图支持,默认关闭,
            toc:true,

        });
    });
</script>

控制台页面

功能:展示用户登入的ip和时间,对每个文章可以有删除文章,编辑文章等等等

思路

这个没有什么难的,无非是对文章增删改

写好数据库操作工具类JdbcUtil.java,

写好ArticleDao类,内部使用JdbcUtil类static方法专门封装好对文章增删改查的static方法,封装ArticleService根据逻辑和数据具体操作DAO

通过BlogServlet跳转到此页面,servlet里要在request里set 包含所有文章的Article类列表对象,然后这个控制台页面.jsp内部插入java脚本循环为每个页面生成两个超链接,分别是访问BlogServlet来跳转到编辑页面,和访问BlogServlet来删除页面,可以通过get方法传参数比如”xxx/BlogServlet?action=delete&ArticleId=7”,然后BlogServlet用一个策略设计模式(其实也就if else if else)实现对应功能即可,例子见上面有类似的

展示用户登入的ip和时间,就不用数据库存储了,开个单例类,内部static int变量初始为零和static方法给变量自增,static ip,时间列表和对应方法

开个过滤器给网页主页,访问一次就填进去,jsp页面直接插入脚本读这个类里的数据展示即可

过滤器内部可以使用这个函数获得Ip

    public String getIpAddr(HttpServletRequest request)
    {
        String ip = request.getHeader(" x-forwarded-for ");
        if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip))
        {
        ip = request.getHeader(" Proxy-Client-IP ");
        }
        if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
            ip = request.getHeader(" WL-Proxy-Client-IP ");
        }
        if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;

    }

其它功能想到了就加就完了

常用页面跳转实现

一,  使用href超链接标记 (客户端跳转)

通常写到a标签里即可,来完成指定位置的动态跳转比较方便

代码:<a href="new.jsp">跳转</a>

二,  提交表单  (客户端跳转)

    <form name="form" method="post" action="page2.jsp">

          <input type="submit" value="跳转1">

      </form>

三,  Javascrip事件       (客户端跳转)

    <input type="button" value="跳转2" οnclick="next()">

    <script type="text/javascript">

          function next(){undefined

              window.location = "page2.jsp";

          }

     </script>

四,  使用response对象     (客户端跳转)(重定向)

   

直接使用sendRedirect()重定向, 重定向后在浏览器地址栏上会出现重定向页面的URL.代码:

    <%  response.sendRedirect("page2.jsp"); %>

    说明: sendredirect()中是可以带参数的,例如sendredirect("url?name="+name);我们可以在跳转的时候传入参数.

    此外,一般response.sendRedirect()之后紧跟一句 return;我们已经知response.sendRedirect是通过浏览器来做转向的,所以只有在页面处理完成后,才会有实际的动作。既然已经要做转向了,那么后的输出就已经没有意义了,而且有可能会因为后面的输出导致转向失败。

使用setHeader()方法,直接修改地址栏来实现页面的重定向

     <%  

           response.setHeader("Refresh","1;url=page2.jsp");        

     %> 

     标准格式: response.setHeader("Refresh","等待的秒数;url=绝对路径或者相对路径");上例是等待1秒之后跳转.

五,  使用forward动作标记   (服务器端跳转)(转发)

    jsp自带的forword标签来实现跳转    <jsp:forward page="page2.jsp" /> 

六,   使用RequestDispatcher类 (服务器端跳转)(转发) 

    <% request.getRequestDispatcher(“page2.jsp”).forward(request, response);%> 

response重定向和forward跳转和RequestDispatcher的区别

(1) response重定向

    执行完页面的所有代码,再跳转到目标页面。
    跳转到目标页面后,浏览器地址栏中的URL会改变。
    在浏览器端重定向。
    可以跳转到其它服务器上的页面,response.sendRedirect(“http://www.baidu.com”)

(2) forward跳转

    forward动作标记之后的代码,不再执行,直接跳转到目标页面。
    跳转到目标页面后,浏览器地址栏中的URL不会改变。
    在服务器端重定向。
    无法跳转到其它服务器上的页面。

    指定目标页面时,既可以使用绝对路径,也可以使用相对路径。

(3) RequestDispatcher跳转

    执行完所有代码,包括RequestDispatcher之后的所有代码,再跳转到目标页面。
    跳转到目标页面后,浏览器地址栏中的URL不会改变。
    在服务器端重定向。
    无法跳转到其它服务器上的页面。

    指定目标页面时,只能使用绝对路径。

部署!!!!!!!!!!!!!!!!!!!!

编程用电脑的环境及项目

云服务器初始状态

Step1 云服务器配置javajdk

直接sudo apt-get install openjdk-xx

xx改成对应版本,详细命令我忘了,但是直接输sudo apt-get install openjdk或者java的话会跳出来让你选的

一般会装在/usr/lib/jvm

然后配置一下%JAVA_HOME对应上java安装目录….其它java环境变量类似

就是在/etc/profile末尾加几行,下面给个例子,注意下面例子中的JAVA_HOME路径改成你自己的

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.144-0.b01.el7_4.x86_64
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

Step2 云服务器配置mysql8.0

官网下载对应版本connector,后缀为deb

安装后找到mysql-connector-java-xxxx.jar,复制到java安装目录的lib下,另外本地web项目的WEBINF/lib下也有有这个,tomcat的lib里也要有….因为我不清楚到时究竟从哪调用的

给个例子

cp /usr/share/java/mysql-connector-java-8.0.20.jar  /usr/local/hive/lib #将jar包拷贝到/hive/lib目录下

本地idea用图形化工具找到对应的数据库对着相应数据库用mysqldump导出,传到云端,mysql创建和同名数据库并use 后,用mysql source一下导入即可,不用辛苦重新创库再创表,加数据

Step3 云服务器配置tomcat

这里转载一下官网配置教程 ,可以用的,不过要把一切和tomcat文件夹名称有关的自己调整一下,因为版本不同,文件夹名字也不同

编辑文件我个人习惯用nano, ctrl+s 保存ctrl+x退出,官网用的vim

步骤一:准备工作

  1. 在安全组入方向添加规则放行所需端口。

    具体操作,请参见添加安全组规则。本示例中,需要放行SSH协议的22端口、HTTP协议的80端口、HTTPS协议的443端口和Tomcat默认使用的8080端口。

  2. 远程连接Linux实例。

    具体操作,请参见通过密码或密钥认证登录Linux实例

  3. 关闭防火墙。

    1. 运行**systemctl status firewalld**命令查看当前防火墙的状态。

      查看防火墙状态

      • 如果防火墙的状态参数是inactive,则防火墙为关闭状态。
      • 如果防火墙的状态参数是active,则防火墙为开启状态。本示例中防火墙为开启状态,因此需要关闭防火墙。
    2. 关闭防火墙。如果防火墙为关闭状态可以忽略此步骤。

      • 如果您想临时关闭防火墙,运行以下命令。

        放大查看复制代码

        systemctl stop firewalld
        

        说明 该操作只是暂时关闭防火墙,下次重启Linux后,防火墙还会开启。

      • 如果您想永久关闭防火墙,需要依次运行以下命令。

        1. 关闭当前运行中的防火墙。

          放大查看复制代码

          systemctl stop firewalld
          
        2. 关闭防火墙服务,在下次重启实例后,防火墙服务将不会开机自启动。

          放大查看复制代码

          systemctl disable firewalld
          

        说明 该操作会关闭防火墙服务,当您重新启动实例后,防火墙将会默认保持关闭状态。 如果您想重新开启防火墙,具体操作,请参见firewalld官网信息

  4. 关闭SELinux。

    1. 运行命令以下命令查看SELinux的当前状态。

      放大查看复制代码

      getenforce
      

      查看结果示例,如下图所示:查看SELinux状态

      • 如果SELinux状态参数是Disabled, 则SELinux为关闭状态。
      • 如果SELinux状态参数是Enforcing,则SELinux为开启状态。本示例中SELinux为开启状态,因此需要关闭SELinux。
    2. 关闭SELinux。如果SELinux为关闭状态可以忽略此步骤。

      • 如果您想临时关闭SELinux,运行以下命令。

        放大查看复制代码

        setenforce 0
        

        说明 该操作只是暂时关闭SELinux,下次重启Linux后,SELinux还会开启。

      • 如果您想永久关闭SELinux,运行以下命令打开SELinux配置文件。

        放大查看复制代码

        vi /etc/selinux/config
        

        /etc/selinux/config

        文件内,将光标移动到

        SELINUX=enforcing
        

        一行,按

        i

        键进入编辑模式,修改为

        SELINUX=disabled
        

        ,然后按

        Esc

        键,再输入

        :wq
        

        并回车,保存关闭SELinux配置文件。

        说明 如果您想重新开启SELinux,具体操作,请参见开启或关闭SELinux

    3. 重启系统使设置生效。

  5. 基于安全考虑,创建一般用户www来运行Tomcat。

    放大查看复制代码

    useradd www
    
  6. 运行以下命令创建网站根目录。

    放大查看复制代码

    mkdir -p /data/wwwroot/default
    
  7. 将网站根目录的所属用户设置为www。

    放大查看复制代码

    chown -R www.www /data/wwwroot
    

步骤二:安装JDK1.8

  1. 通过yum命令查找JDK1.8软件包。

    放大查看复制代码

    yum -y list java*
    
  2. 安装列表中的JDK1.8软件包。

    放大查看复制代码

    yum -y install java-1.8.0-openjdk-devel.x86_64
    
  3. 查看JDK版本。

    放大查看复制代码

    java -version
    

    本示例中版本信息如下所示。

    放大查看复制代码

    openjdk version "1.8.0_292"
    OpenJDK Runtime Environment (build 1.8.0_292-b10)
    OpenJDK 64-Bit Server VM (build 25.292-b10, mixed mode)
    
  4. 配置环境变量。

    1. 打开配置文件。

      放大查看复制代码

      vim /etc/profile
      
    2. 在配置文件末尾,按i进入编辑模式。

    3. 添加以下信息。

      说明JAVA_HOME值为当前JDK安装的路径。本示例中,运行命令**cd /usr/lib/jvm/**进入jvm路径下,然后运行**ls**查看JDK安装后文件的路径。

      放大查看复制成功

      JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.al7.x86_64
      PATH=$PATH:$JAVA_HOME/bin
      CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
      export JAVA_HOME CLASSPATH PATH
      
    4. 按下Esc键,输入:wq并回车以保存并关闭文件。

    5. 立即生效环境变量。

      放大查看复制代码

      source /etc/profile
      

步骤三:安装Apache Tomcat

  1. 运行以下命令下载Tomcat 8安装包。

    放大查看复制代码

    wget https://apache.claz.org/tomcat/tomcat-8/v8.5.69/bin/apache-tomcat-8.5.69.tar.gz
    

    说明 Tomcat下载地址会更新。新版本的下载地址,请访问Tomcat官网获取。

  2. 解压Tomcat 8安装包。

    放大查看复制成功

    tar -zxvf apache-tomcat-8.5.69.tar.gz
    
  3. 移动Tomcat所在目录。

    放大查看复制代码

    mv apache-tomcat-8.5.69 /usr/local/tomcat/
    
  4. 将文件的所属用户设置为www。

    放大查看复制代码

    chown -R www.www /usr/local/tomcat/
    

    在/usr/local/tomcat/目录下:

    • bin:存放Tomcat的一些脚本文件,包含启动和关闭Tomcat服务脚本。
    • conf:存放Tomcat服务器的各种全局配置文件,其中最重要的是server.xml和web.xml。
    • webapps:Tomcat的主要Web发布目录,默认情况下把Web应用文件放于此目录。
    • logs:存放Tomcat执行时的日志文件。
  5. 配置server.xml文件。

    1. 运行以下命令切换到/usr/local/tomcat/conf/目录。

      放大查看复制代码

      cd /usr/local/tomcat/conf/
      
    2. 运行以下命令重命名server.xml文件。

      放大查看复制代码

      mv server.xml server.xml_bk
      
    3. 新建一个server.xml文件。

      1. 运行以下命令创建并打开

        server.xml

        文件。

        放大查看复制代码

        vi server.xml
        
      2. 按下

        i

        键,添加以下内容。

        放大查看复制成功

        <?xml version="1.0" encoding="UTF-8"?>
        <Server port="8006" shutdown="SHUTDOWN">
        <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
        <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
        <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
        <Listener className="org.apache.catalina.core.AprLifecycleListener"/>
        <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
         type="org.apache.catalina.UserDatabase"
         description="User database that can be updated and saved"
         factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
         pathname="conf/tomcat-users.xml"/>
        </GlobalNamingResources>
        <Service name="Catalina">
        <Connector port="8080"
         protocol="HTTP/1.1"
         connectionTimeout="20000"
         redirectPort="8443"
         maxThreads="1000"
         minSpareThreads="20"
         acceptCount="1000"
         maxHttpHeaderSize="65536"
         debug="0"
         disableUploadTimeout="true"
         useBodyEncodingForURI="true"
         enableLookups="false"
         URIEncoding="UTF-8"/>
        <Engine name="Catalina" defaultHost="localhost">
        <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
          resourceName="UserDatabase"/>
        </Realm>
        <Host name="localhost" appBase="/data/wwwroot/default" unpackWARs="true" autoDeploy="true">
        <Context path="" docBase="/data/wwwroot/default" debug="0" reloadable="false" crossContext="true"/>
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
        prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        </Host>
        </Engine>
        </Service>
        </Server>
        
      3. 按Esc键,输入:wq并回车以保存并关闭文件。

  6. 设置JVM内存参数。

    1. 运行以下命令创建并打开/usr/local/tomcat/bin/setenv.sh文件。

      放大查看复制成功

      vi /usr/local/tomcat/bin/setenv.sh
      
    2. 按下i键,添加以下内容。

      指定JAVA_OPTS参数,用于设置JVM的内存信息以及编码格式。

      放大查看复制成功

      JAVA_OPTS='-Djava.security.egd=file:/dev/./urandom -server -Xms256m -Xmx496m -Dfile.encoding=UTF-8'
      
    3. 按下Esc键,输入:wq并回车以保存并关闭文件。

  7. 设置Tomcat自启动脚本。

    1. 运行以下命令下载Tomcat自启动脚本文件。

      说明 该脚本来源于社区,仅供参考。阿里云对其可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。如果您运行**wget**命令下载失败,您可以通过浏览器访问https://raw.githubusercontent.com/oneinstack/oneinstack/master/init.d/Tomcat-init直接获取脚本内容。

      放大查看复制成功

      wget https://raw.githubusercontent.com/oneinstack/oneinstack/master/init.d/Tomcat-init
      
    2. 运行以下命令移动并重命名Tomcat-init。

      放大查看复制代码

      mv Tomcat-init /etc/init.d/tomcat
      
    3. 运行以下命令为/etc/init.d/tomcat添加可执行权限。

      放大查看复制成功

      chmod +x /etc/init.d/tomcat
      
    4. 运行以下命令设置启动脚本JAVA_HOME。

      注意 脚本中JDK的路径信息必须与您安装的JDK路径保持一致,否则Tomcat会启动失败。

      放大查看复制成功

      sed -i 's@^export JAVA_HOME=.*@export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.1.al7.x86_64@' /etc/init.d/tomcat
      
  8. 依次运行以下命令设置Tomcat开机自启动。

    1. 放大查看复制成功

      chkconfig --add tomcat
      
    2. 放大查看复制成功

      chkconfig tomcat on
      
  9. 运行以下命令启动Tomcat。

    放大查看复制代码

    service tomcat start
    

步骤四:部署测试项目并验证

将需要部署的Java Web项目文件WAR包上传到网站根目录下,并将网站根目录下文件所属用户改为www。您可以使用支持文件传输功能的远程连接工具或搭建FTP站点上传项目文件。本示例中,网站根目录为/data/wwwroot/default,运行以下命令直接在网站根目录下新建一个Tomcat测试页面,并进行访问。

  1. 部署测试文件。

    放大查看复制成功

    echo Tomcat test > /data/wwwroot/default/index.jsp
    
  2. 在本地浏览器地址栏中输入http://公网IP:8080进行访问。

    返回页面如下图所示,表示安装成功。返回结果

Step4 域名绑定+解析

域名和云解析都要购买,买完后有教程(简单到都不用看),把域名映射到自己的云服务器的公网ip即可

然后在云服务器tomcat安装目录里找到server.xml,把端口号从8080改成80即可,这时候访问公网ip立马就到主页了,对应的访问域名也就立马到主页了

Step5 本地web项目放到云服务器

直接在idea-build-里编译artifects,把输出的东西放到tomcat解析的目录里即可(生成出的东西,就是你项目中的web目录下的东西,只不过web下面的WEB-INF包含了你的项目下src(后台代码,servlet,实体类等等的.class版本),按照上面流程走的话就是把输出的东西放到云服务器里的/data/wwwroot/default下,这个是根据server.xml里<host>标记内的设置来的。

这里我复制过来看一下,那个appBase就是你的项目目录,在例子中的设置情况下,appBase文件夹里必须直接就是你的jsp文件和包含jsp的目录,同时直接有个WEB-INF包含你的class

<Host name="localhost" appBase="/data/wwwroot/default" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="/data/wwwroot/default" debug="0" reloadable="false" crossContext="true"/>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

xml里端口8080改成80后

访问公网ip,直接跳转到目录下index.jsp,如果没改那就手动在后面填:8080

域名同理

另外,要确保项目中各种链接,都是使用request.getContextPath()开头,或者String context = request.getContextPath();然后用context开头,确保是绝对路径,否则报404的时候才知道原来路径有问题

上次修改时间:2022-02-12 21:14:58

>