《Web应用技术》第十一次课后作业

主要内容:使用JWT令牌和过滤器实现登录校验

原理:jwt令牌是一串简单的字符串,定义了简答,自包含的格式,通信双方以json格式安全地传输信息。因为有数字签名的存在,这些信息是可靠的。jwt令牌通常包含头部,载荷和数字签名。其具体应用场景一般是登录验证,流程为浏览器发生登录请求,登录成功后,服务端生成jwt令牌并下发给浏览器,后续浏览器的每次请求都要携带jwt令牌,服务端统一拦截所有请求,并检查是否携带jwt令牌以及是否有效,若有效,则允许访问web资源,若非法,则返回错误响应信息。其中,判断jwt令牌是否有效的环节通常有过滤器来完成。过滤器是javaweb三大组件之一,可以拦截所有请求,并实现某些特殊的通用的功能,如登录校验。其流程是拦截请求,执行放行前逻辑,放行,然后访问web资源,返回过滤器,执行放行后逻辑,通常配合webfilter注解和servletconponentscan注解,实现对指定路径进行拦截和对servlet组件支持。另外,可以配置多个过滤器,组成过滤器链,实现更复杂的功能。

目录结构

FilterUtils

package com.example.sun.utils;

import com.alibaba.fastjson.JSONObject;
import com.example.sun.pojo.Result;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.io.IOException;
@WebFilter(urlPatterns = "/*")
@Slf4j
public class FilterUtils  implements Filter {



    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req=(HttpServletRequest)  servletRequest;
        HttpServletResponse res=(HttpServletResponse) servletResponse;

        String url=req.getRequestURI().toString();

        if(url.contains("login")){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        if(url.contains("add")){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        if(url.contains("edit")){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        if(url.contains("show")){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        String jwt=req.getHeader("token");
        log.info(jwt);
        if(!StringUtils.hasLength(jwt)){
            Result error=Result.error("NotLoginjwt");
            String notLogin= JSONObject.toJSONString(error);
            res.getWriter().write(notLogin);
            return;
        }

        try{
            JwtUtils.parseJWT(jwt);
        } catch(Exception e){
            e.printStackTrace();
            Result error=Result.error("NotLoginerror");
            String notLogin= JSONObject.toJSONString(error);
            res.getWriter().write(notLogin);
            return;
        }

        filterChain.doFilter(servletRequest, servletResponse);


    }


}

JwtUtils

package com.example.sun.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtils {
    private static String signKey="sunfan";
    private static Long expire =43200000L;
    /*生成JWT令牌*/
    public static String generateJwt(Map<String,Object> claims){
        String jwt= Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256,signKey)
                .setExpiration(new Date(System.currentTimeMillis()+expire))
                .compact();
        return jwt;
    }
    /*解析JWT令牌*/
    public static Claims parseJWT(String jwt) {
        Claims claims=Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }

}

poetcontroller

package com.example.sun.controller;


import com.example.sun.pojo.Poet;
import com.example.sun.pojo.Result;
import com.example.sun.pojo.User;
import com.example.sun.service.PoetserviceA;
import com.example.sun.pojo.Poetbean;
import com.example.sun.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;


@RestController
public class poetcontroller {
    @Autowired
    private PoetserviceA poetserviceA;
    @GetMapping("/findCondition/{page}/{pageSize}")
    /*@GetMapping("/poet")*/
    public Result select(@PathVariable Integer page,
                         @PathVariable Integer pageSize,
                         String name,String style) {
        Poetbean poetbean=poetserviceA.page(page,pageSize,name,style);
        return Result.success(poetbean);
    }
    @GetMapping("/poet/{page}/{pageSize}")
    /*@GetMapping("/poet")*/
    public Result select(  @PathVariable Integer page,
                           @PathVariable Integer pageSize) {
        Poetbean poetbean=poetserviceA.page(page,pageSize);
        return Result.success(poetbean);
    }

    @RequestMapping("/delete")
    public int deletePoet(Integer id){
       return poetserviceA.delete(id);
    }

    @RequestMapping("/update")
    public  Result updatePoet(@RequestBody Poet poet){
        boolean r = poetserviceA.update(poet);

        if(r) {
            // 成功  code==1
            return Result.success();
        } else {
            // 失败  code==0
            return Result.error("更新失败");
        }
    }

    @RequestMapping("/findById/{id}")
    public Result poetfindById(@PathVariable("id") Integer id) {
        return  Result.success(poetserviceA.poetfindById(id));
    }

    @RequestMapping("/insert")
    public  Result insert( @RequestBody Poet poet){
        boolean result = poetserviceA.insert(poet);
        if(result) {
            // 成功  code==1
            return Result.success();
        } else {
            // 失败  code==0
            return Result.error("添加失败");

        }
    }
    @PostMapping("/login")
    public Result login(@RequestBody User user) {
        //调用业务层:登录功能
        User loginUser=poetserviceA.getByNameAndPassword(user);
        //判断:登录用户是否存在
        if(loginUser !=null ){
            //自定义信息
            Map<String , Object> claims = new HashMap<>();
            claims.put("password",loginUser.getPassword());
            claims.put("name",loginUser.getName());
            //使用JWT工具类,生成身份令牌
            String token = JwtUtils.generateJwt(claims);
            System.out.println(token);

            Map<String, String> response = new HashMap<>();
            response.put("token", token);

            return Result.success(token);

        }
        return Result.error("用户名或密码错误");
    }



}

show.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>查询出所有并分页控件显示</title>
    <link rel="stylesheet" href="js/element.css">
</head>
<body style="align:center">

<div id="app" style="width: 80%;align:center"  >
    <div >
    <h1 align="center">诗人信息</h1>
    <p align="center">
        <el-form :inline="true" :model="formInline" class="demo-form-inline">
            <el-form-item label="姓名">
                <el-input v-model="formInline.name" placeholder="姓名"></el-input>
            </el-form-item>
            <el-form-item label="风格">
                <el-input v-model="formInline.style" placeholder="风格"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" icon="el-icon-search" @click="onSubmit" >查询</el-button>
            </el-form-item>
            <el-form-item>

            </el-form-item>
            <el-form-item>

            </el-form-item>
            <el-form-item>

            </el-form-item>
            <el-form-item>

            </el-form-item>
            <el-form-item>

            </el-form-item>
            <el-form-item>

            </el-form-item>
            <el-form-item>
                <el-button type="danger" icon="el-icon-switch-button" @click="logout">退出</el-button>
            </el-form-item>
        </el-form>
    </p>
    <p align="right">
        <el-form :inline="true" :model="formInline" class="demo-form-inline">
        <el-form-item>
            <el-button type="primary" icon="el-icon-plus" @click="insert">添加</el-button>

        </el-form-item>
        </el-form>
    </p>
    <el-table
            :data="tableData.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))"
            style="width: 100%;align:center;font-size: 20px">
        <el-table-column
                label="id"
                prop="id">
        </el-table-column>
        <el-table-column
                label="姓名"
                prop="name">
        </el-table-column>
        <el-table-column
                label="性别"
                prop="gender">
        </el-table-column>
        <el-table-column
                label="朝代"
                prop="dynasty">
        </el-table-column>
        <el-table-column
                label="头衔"
                prop="title">
        </el-table-column>
        <el-table-column
                label="风格"
                prop="style">
        </el-table-column>
        <el-table-column
                align="right">
            <template slot="header" slot-scope="scope">
                <el-input
                        v-model="search"
                        size="mini"
                        placeholder="输入关键字搜索"/>
            </template>
            <template slot-scope="scope">
                <el-button size="mini"
                @click="handleEdit(scope.row.id)">编辑</el-button>
                <el-button
                        size="mini"
                        type="danger"
                        @click="handleDelete(scope.row.id)">删除</el-button>
            </template>
        </el-table-column>
    </el-table>
    <p align="center">
        <el-pagination
                layout="total, sizes, prev, pager, next, jumper"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
                :current-page="currentPage"
                :page-sizes="[3, 5, 10, 20]"
                :page-size="pageSize"
                :total="total">
        </el-pagination>
    </p>
</div>
<!--    <div  v-else-if="tableData1.code==0" >-->
<!--    {{tableData1.code}}无权限访问-->

</div>

<!-- 引入组件库 -->
<script src="js/jquery.min.js"></script>
<script src="js/vue.js"></script>
<script src="js/element.js"></script>
<script src="js/axios-0.18.0.js"></script>
<script>
    new Vue({
        el:"#app",
        data: {
            search: '',
            currentPage: 1,
            pageSize: 3,
            total: null,
            token:localStorage.getItem("token"),
            tableData: [],
            tableData1:[],
            tableData2:[],
            formInline: {
                name: '',
                style: ''
            }
        },
        methods: {
            logout(){
                window.open('http://localhost:8080/login.html')
            },
            insert(){
                window.open('http://localhost:8080/add.html')
            },
            handleEdit(id) {

                window.open('http://localhost:8080/edit.html?id='+id)

                console.log(index, row);
            },
            handleDelete(id) {
                var _thisd = this;
                if (window.confirm("确定要删除该条数据吗???")) {
                    axios.post('/delete?id=' + id,{},{headers:{"token":this.token}})
                        .then(function (response) {
                            alert("删除成功")
                            _thisd.findAll();
                        })
                        .catch(function (error) {
                            console.log(error);
                        });
                }
                console.log(index, row);
            },
            handleSizeChange(val) {
                this.pageSize = val;
                this.findAll();
                console.log(`每页 ${val} 条`);

            },
            handleCurrentChange(val) {
                this.currentPage = val;
                this.findAll();
                console.log(`当前页: ${val}`);

            },
            onSubmit() {

                var url = `/findCondition/${this.currentPage}/${this.pageSize}?name=${encodeURIComponent(this.formInline.name)}&style=${encodeURIComponent(this.formInline.style)}`

                console.log(this.formInline.name);
                console.log(this.formInline.style);


                axios.get(url,{headers:{"token":this.token}}

                )
                    .then(res =>{
                        this.tableData = res.data.data.rows;
                        this.total=res.data.data.total;
                        console.log(this.tableData);
                        console.log(this.total);
                        console.log(this.token);
                    })
                    .catch(error=>{
                        console.error(error);
                    })

            },


            findAll() {
                /*var url1 = `/index1`
                axios.get(url1)
                    .then(response => {
                        this.tableData1 = response.data;
                        console.log(this.tableData1);

                    })
                    .catch(error=>{
                        console.error(error);
                    })*/
                    var url = `/poet/${this.currentPage}/${this.pageSize}`
                    axios.get(url,{headers:{"token":this.token}})
                        .then(res => {
                            this.tableData = res.data.data.rows;
                            this.total = res.data.data.total;
                            console.log(this.tableData);
                            console.log(this.total);
                        })
                        .catch(error => {
                            console.error(error);
                        })


            },
        },
        created(){
            this.findAll();
        }

    })


</script>

</body>
</html>

add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <script src="./js/axios-0.18.0.js"></script>

</head>
<body>
<div id="app">
    <table border="1">

        <tr>
            <td>id</td>
            <td><input type="text" v-model="poet.id"> </td>
        </tr>
        <tr>
            <td>姓名</td>
            <td><input type="text" v-model="poet.name"> </td>
        </tr>
        <tr>
            <td>性别</td>
            <td><input type="text" v-model="poet.gender"></td>
        </tr>
        <tr>
            <td>朝代</td>
            <td><input type="text" v-model="poet.dynasty"></td>
        </tr>
        <tr>
            <td>头衔</td>
            <td><input type="text" v-model="poet.title"></td>
        </tr>
        <tr>
            <td>风格</td>
            <td><input type="text" v-model="poet.style"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="button" @click="addpoet" value="增加"> </td>
        </tr>
    </table>

</div>
</body>
<script>
    new Vue({
        el: '#app',
        data: {
            token:localStorage.getItem("token"),
            poet: {
                "id":"",
                "name":"",
                "gender":"",
                "dynasty":"",
                "title":"",
                "style":""
            }        //详情

        },
        methods: {

            addpoet() {
                var url = 'insert'
                axios.post(url,this.poet,{headers:{"token":this.token}})
                    .then(res => {
                        var baseResult = res.data
                        if(baseResult.code == 1) {
                            // 成功
                            location.href = 'show.html'
                        } else {
                            // 失败
                            alert(baseResult.message)
                        }
                    })
                    .catch(err => {
                        console.error(err);
                    })
            }
        },

    })
</script>

</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>登录后信息放在session中</title>
  <!-- 引入组件库 -->
  <script src="js/jquery.min.js"></script>
  <script src="js/vue.js"></script>
  <script src="js/element.js"></script>
  <script src="js/axios-0.18.0.js"></script>
  <link rel="stylesheet" href="js/element.css">

</head>
<body>
<div id="app" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
  <el-alert
          v-if="showError"
          title="账号或密码错误"
          type="error"
          center
          show-icon
          style="position: absolute; top: 50%; left: 50%; transform: translate(-42%, -400%);">
  </el-alert>
  <el-form ref="form" :model="form" label-width="80px">
    <el-form-item label="姓名">
      <el-input v-model="form.name"></el-input>
    </el-form-item>
    <el-form-item label="密码">
      <el-input v-model="form.password"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmit" style="font-size: 20px; padding: 10px 20px; width: 200px;">登录</el-button>
    </el-form-item>
  </el-form>

</div>
<script>
  new Vue({
    el:"#app",
    data:{
      showError: false,

      form: {
        name: '',
        password: ''
      },
      tableData: []
    },
    methods:{
      onSubmit() {
        var url = `/login`
        console.log(this.form.name);
        console.log(this.form.password);

        axios.post(url, {
          name: this.form.name,
          password: this.form.password
        })
                .then(response => {

                    this.tableData = response.data;
                    console.log(this.tableData);
                    const token=response.data.data;
                    localStorage.setItem("token",token);

                    console.log(token);
                    if(this.tableData.data!=null) {
                      location.href = 'show.html'
                    }



                   if(response.data.code==0){
                    this.tableData=response.data;
                    this.showError=true;
                    setTimeout(() => {
                      this.showError = false; // 隐藏错误提示
                    }, 3000);
                  }
                })
                .catch(error=>{
                  console.error(error);
                })
      }
    }

  })

</script>



</body>
</html>

edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.js"></script>
    <script src="./js/axios-0.18.0.js"></script>

</head>
<body>
<div id="app">
    <table border="1">
        <tr>
            <td>编号</td>
            <td><input type="text" v-model="this.id"> </td>
        </tr>
        <tr>
            <td>姓名</td>
            <td><input type="text" v-model="poet.name"> </td>
        </tr>
        <tr>
            <td>性别</td>
            <td>
                <input type="text"  v-model="poet.gender" >
            </td>
        </tr>
        <tr>
            <td>朝代</td>
            <td><input type="text" v-model="poet.dynasty"> </td>
        </tr>
        <tr>
            <td>头衔</td>
            <td><input type="text" v-model="poet.title"> </td>
        </tr>
        <tr>
            <td>风格</td>
            <td><input type="text" v-model="poet.style"> </td>
        </tr>

        <tr>
            <td></td>
            <td><input type="button" @click="updatePoet" value="更新"> </td>

        </tr>
    </table>
</div>


</body>
<script>
    new Vue({
        el: '#app',
        data: {
            token:localStorage.getItem("token"),
            id: '',
            poet: {},        //详情
        },
        methods: {
            selectById() {
                //${this.id}
                var url = `findById/${this.id}`  //注意这里是反引号
                //反引号(backticks,也称为模板字符串或模板字面量)是ES6(ECMAScript 2015)中引入的一种新字符串字面量功能,
                // 它允许您在字符串中嵌入表达式。反引号用`(键盘上通常位于Tab键上方)来界定字符串的开始和结束。
                axios.get(url,{headers:{"token":this.token}})
                    .then(response => {
                        var baseResult = response.data
                        if(baseResult.code == 1) {
                            this.poet = baseResult.data
                        }
                    })
                    .catch( error => {})
            },
            updatePoet() {
                var url = 'update'
                axios.put(url,this.poet,{headers:{"token":this.token}})
                    .then(res => {
                        var baseResult = res.data
                        if(baseResult.code == 1) {
                            // 成功
                            location.href ='show.html'
                        } else {
                            // 失败
                            alert(baseResult.message)
                        }
                    })
                    .catch(err => {
                        console.error(err);
                    })
            },


        },
        created() {
            // 获得参数id值
            this.id = location.href.split("?id=")[1]
            // 通过id查询详情
            this.selectById()
        },

    })



</script>

</html>

效果展示

未登录时访问

登录后访问

说明

需要注意的时候,访问html页面,本身也是一种请求,不是所有的请求只在controller中体现。开始的时候没有把访问html页面作为一种请求,导致登录成功后跳转到show.html页面时,页面始终返回未登录信息,没有组件和数据展示。后续在FilterUtils通过编写以下代码解决:

尽管如此,项目仍存在问题,一方面是前端页面跳转的实现有待优化;另一方面后端过滤器对截取特定路径的控制有待简化。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值