springboot实现登录demo
实现简单的登录功能
实体类
定义实体类为User3类。
使用@Data:提供类的get,set,equals,hashCode,canEqual,toString方法;
使用@AllArgsConstructor:提供类的全参构造
使用@NoArgsConstructor:提供类的无参构造
类代码如下
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User3
{
private Integer id;
private String userName;
private String password;
}
JWT核心类
JWT认证流程如图所示

可见服务器端需要做两件事,一是根据用户信息创建对应的JWT密钥,另一个是根据对应的密钥解码用户信息。
JWT token主要由三个部分组成,主要包括Header,Payload,Signature。通过“."间隔开来。
- Header:包括两部分信息,令牌的类型(typ),签名算法(alg)。
- Payload:也称为声明(Claims),包含JWT的主要信息,主要是用户身份信息,权限等,是一个JSON对象。
- Signature:使用头部使用的算法和密钥对头部和载荷进行签名所生成的一部分,用来验证真实性和完整性。
签名主要是先将头部和载荷转换为Json格式,并进行base64编码,最后用.拼接在一起
如
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"sub": "1234567890", "name": "John Doe", "admin": true}
转换后为
Encoded Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Encoded Payload: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
使用指定的算法对Encoded Header.Payload进行签名。最后再拼接在一起
验证时使用JWTVerifier实例,使用 HMAC256 算法和指定的密钥(SECRET)来验证签名。
主要代码如下
public class JwtUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
/**
* 密钥
*/
private static final String SECRET = "my_secret";
/**
* 过期时间
**/
private static final long EXPIRATION = 1800L;//单位为秒
/**
* 生成用户token,设置token超时时间
*/
public static String createToken(User3 user) {
//过期时间30分钟
Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create()
.withHeader(map)// 添加头部
//可以将基本信息放到claims中
.withClaim("id", user.getId())//userId
.withClaim("userName", user.getUserName())//userName
.withClaim("password", user.getPassword())//password
.withExpiresAt(expireDate) //超时设置,设置过期的日期
.withIssuedAt(new Date()) //签发时间
.sign(Algorithm.HMAC256(SECRET)); //SECRET加密
System.out.println("生成的token:"+token);
return token;
}
/**
* 校验token并解析token
*/
public static Map<String, Claim> verifyToken(String token) {
DecodedJWT jwt = null;
System.out.println("识别的token:"+token);
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
logger.error(e.getMessage());
logger.error("token解码异常");
return null;
}
return jwt.getClaims();
}
}
JWT过滤器
使用过滤器可以提供一种轻量级,安全,高效的方式来处理身份验证和授权,可以简化开发流程。
可以避免服务器每次请求都进行状态管理,无需每次都查询数据库验证用户身份或访问权限。
使用过滤器前我们首先要进行注册
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<JwtFilter> jwtFilter() {
FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/secure/*");
return registrationBean;
}
}
由于用户在每次请求时都会带上Authorization的token,所以要提取出对应的token,识别出用户对应的信息后再进行下一步处理。
整个过程处于过滤器链中,对于 OPTIONS 请求,直接放行并调用 chain.doFilter(request, response);
过滤器的代码如下。
public class JwtFilter implements Filter
{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
String temp = request.getHeader("authorization");
System.out.println("temp:" + temp);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
final String token = request.getHeader("authorization");
System.out.println("token:" + token);
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
chain.doFilter(request, response);
}
else {
if (token == null) {
response.getWriter().write("没有token!");
return;
}
Map<String, Claim> userData = JwtUtil.verifyToken(token);
if (userData == null) {
response.getWriter().write("token不合法!");
return;
}
Integer id = userData.get("id").asInt();
String userName = userData.get("userName").asString();
String password= userData.get("password").asString();
//拦截器 拿到用户信息,放到request中
request.setAttribute("id", id);
request.setAttribute("userName", userName);
request.setAttribute("password", password);
chain.doFilter(req, res);
}
}
@Override
public void destroy() {
}
}
或许会有个疑问,那就是这里代码也调用了chain.doFilter(req, res);会导致递归等问题吗。
其实并不会,每个过滤器都会调用chain.doFilter(req, res)方法把请求传递给下一个过滤器,直到最后一个过滤器调用目标Servlet。整个过程处于线性并且有终点。
一般来说,过滤器链的工作流程如下:
- 过滤器链初始化:当一个请求到达时,服务器根据配置初始化过滤器链。
- 过滤器链顺序执行:请求进入第一个过滤器,第一个过滤器在逻辑中调用chain.doFilter(request, response); 将请求传递给下一个过滤器。
- 传递到目标资源:过程一直持续,直到最后一个过滤器调用chain.doFilter(request, response)传递给最终的目标Servlet或JSP页面来处理。
- 响应返回:目标资源生成响应后,响应会逆向通过所有过滤器回传给客户端,每个过滤器都有机会在返回路径上再次处理响应。
下面以一个例子来说明
public class Filter1 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Filter1: before chain");
chain.doFilter(request, response); // 将请求传递给下一个过滤器
System.out.println("Filter1: after chain");
}
}
public class Filter2 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Filter2: before chain");
chain.doFilter(request, response); // 将请求传递给下一个过滤器
System.out.println("Filter2: after chain");
}
}
public class TargetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("TargetServlet response");
}
}
当请求到达时,控制流执行如下:
Filter1 执行前部分代码(Filter1:before chain)。
Filter1 调用 chain.doFilter(request, response); 将请求传递给 Filter2。
Filter2 执行前部分代码(Filter2: before chain)。
Filter2 调用 chain.doFilter(request, response); 将请求传递给 TargetServlet。
TargetServlet 生成响应。
响应传回 Filter2,执行后部分代码(Filter2: after chain)。
响应传回 Filter1,执行后部分代码(Filter1: after chain)。
最终响应传回给客户端。
如果过滤器链中的最后一个过滤器调用了chain.doFilter(request, response);,请求将被传到目标资源。
解码返回响应
最后为了查看解码是否正确,还需要查看解码后的用户信息
编写类代码如下
@RequestMapping("/secure/getUserInfo")
@UnInterception
public JSONObject login(HttpServletRequest request) {
// final String token = request.getHeader("authorization");
// System.out.println("token:" + token);
Integer id = (Integer) request.getAttribute("id");
System.out.println(id);
String userName = request.getAttribute("userName").toString();
String password = request.getAttribute("password").toString();
HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
assert response != null;
response.setCharacterEncoding("UTF-8");
JSONObject test = new JSONObject();
test.put("id",id);
test.put("userName",userName);
test.put("password",password);
System.out.println("当前用户信息id=" + id + ", userName=" + userName + ", password=" + password);
return test;
}
这里的request是已经完成了过滤后的请求,注意要设置字符编码为UTF-8,否则可能会返回乱码。
static Map<Integer, User3> userMap = new HashMap<>();
static {
User3 user1 = new User3(1,"张三","123456");
userMap.put(1, user1);
User3 user2 = new User3(2,"李四","123123");
userMap.put(2, user2);
}
/**
* 模拟用户 登录
*/
@RequestMapping(value = "/login", method = RequestMethod.GET)
@UnInterception
public String login(@RequestParam String userName, @RequestParam String password)
{
System.out.println("userName: " + userName + ", password: " + password);
for (User3 dbUser : userMap.values()) {
if (dbUser.getUserName().equals(userName) && dbUser.getPassword().equals(password)) {
log.info("登录成功!生成token!");
String token = JwtUtil.createToken(dbUser);
return token;
}
}
return "";
}
测试结果
在Apifox上测试后结果如下。



可以看到正常发送了请求并且解码成功,而且编码是UTF-8
springboot实现登录demo的更多相关文章
- springboot之登录注册
springboot之登录注册 目录结构 pom.xml <?xml version="1.0" encoding="UTF-8"?> <pr ...
- Xamarin.Android再体验之简单的登录Demo
一.前言 在空闲之余,学学新东西 二.服务端的代码编写与部署 这里采取的方式是MVC+EF返回Json数据,(本来是想用Nancy来实现的,想想电脑太卡就不开多个虚拟机了,用用IIS部署也好) 主要是 ...
- 易Android登录Demo
上一页介绍Android项目简单的页面跳转实例,算是对开发环境的熟悉,这一篇将在此基础上增加一些简单的逻辑,实现登录的效果. 登录之前: 登录成功: watermark/2/text/aHR0cDov ...
- SpringBoot注册登录(三):注册--验证账号密码是否符合格式及后台完成注册功能
SpringBoot注册登录(一):User表的设计点击打开链接SpringBoot注册登录(二):注册---验证码kaptcha的实现点击打开链接 SpringBoot注册登录(三):注册 ...
- ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
可以先看下列文章 目录 ElasticSearch 实现分词全文检索 - 概述 ElasticSearch 实现分词全文检索 - ES.Kibana.IK安装 ElasticSearch 实现分词全文 ...
- Springboot实现登录功能
SpringBoot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再 ...
- SpringBoot 并发登录人数控制
通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功能,本文讲解一下在没有使用Security的时候如何手动实现这个功能 dem ...
- [原创]java WEB学习笔记56:Struts2学习之路---Struts 版本的 登录 demo
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- 也谈SSO,一个简单实用的单点登录Demo
关于SSO(单点登录),百度百科解释如下 : “SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.它包括可以将这次主要 ...
- 开源框架bboss单点登录demo跑起来
目前公司新项目要使用一个开源框架bboss的单点登录功能,要将此功能整合到新系统中去,所以我就学习了一下. 首先,进入这个bboss框架作者的博客中,找到相应的session共享,单点登录的博文,看了 ...
随机推荐
- QShop商城--项目介绍
QShop商城-项目介绍 QShop商城,是全新推出的一款轻量级.高性能.前后端分离的电商系统,支持微信小程序,前后端源码100%开源,完美支持二次开发,让您快速搭建个性化独立商城. 技术架构:.Ne ...
- 大数据Hadoop集群的扩容及缩容(动态添加删除节点)
添加白名单和黑名单 白名单,表示在白名单的主机IP地址可以用来存储数据 企业中:配置白名单,可以尽量防止黑客恶意访问攻击. 配置白名单步骤如下:原文:sw-code 1)在NameNode节点的/op ...
- 动态类型语言 VS 静态类型语言
一. 运行期动态修改类型结构 动态编程语言是高级编程语言的一个类别,在计算机科学领域已被广泛应用.它是一类在运行时可以改变其结构的语言:例如新的函数.对象.甚至代码可以被引进,已有的函数可以被删除或是 ...
- 记录一次WhatTheFuck经历
起因 很早之前就一直在维护一个git仓库,平时调研什么组件就会在里面新建一个springboot的工程用来编写示例代码. 最一开始使用的是SpringInitializr,后来网站更新之后,只能生成J ...
- C#复杂类型转为QueryString
使用 visual studio 创建 webapi 项目,并添加 DefaultController.cs,代码如下 public class DefaultController : ApiCont ...
- vue-cli3 项目路由 history 模式部署到 nginx 服务器
1.项目修改vue.config.js增加 publicPath: '/' 2.nginx配置 location / {#访问前端页面 root /data/dist;#vue项目存放路径 index ...
- Mysql 使用 group by 不对 null 做分组
在项目开发查询数据需要将相同的数据做合并处理,但是字段为null,不做合并. 创建表以及添加数据 create table t_student( `id` int not null primary k ...
- 执行nodejs 内置fs模块
执行方式1: 在 cmd 中 任意文件夹路径下输入 node 回车 C:\Users\32991>node ...
- Ubuntu Server LTS 修改网卡ip地址、固定IP
Ubuntu Server LTS 修改网卡ip地址方式.固定IP. 18.04 之前版本通过修改/etc/network/interfaces 方式,18.04 版本开始通过netplan 方式: ...
- Masonry的进阶使用技巧
Masonry是iOS开发中常见的视图约束框架,但是有人对他的使用还是浅尝辄止,接下来会提出几点比较少见但是又十分便捷的使用技巧. mas_greaterThanOrEqualTo mas_great ...