当前位置: 首页 > news >正文

尚庭公寓--------登陆流程介绍以及功能代码

登陆认证

基于Session的认证流程

优点

  1. 用户友好:
    • 用户登录后,无需在每个页面请求中重复输入用户名和密码。• 提供了一种无缝的用户体验,用户在浏览网站时不会频繁被要求重新认证。
  2. 安全性:
    • 服务器端维护用户状态信息,而客户端仅存储一个Session ID,这样可以减少敏感信息的暴露。
    • Session ID通常具有时效性,可以设置过期时间,增加了安全性。
  3. 易于管理:
    • 服务器可以轻松地管理用户会话,例如,可以控制Session的生命周期,包括创建、更新和销毁Session。
    • 可以对Session进行序列化,以便在服务器重启时恢复用户会话。
  4. 灵活性:
    • 可以存储用户特定的信息,如用户角色、权限等,以便在用户会话期间使用。
    • 可以根据需要自定义Session的行为,例如,可以设置Session的有效期、锁定机制等。
  5. 支持分布式部署:
    • 在分布式系统中,可以通过共享Session存储或使用Session复制技术来支持Session的一致性。
  6. 防止CSRF攻击:
    • 通过在Session中存储CSRF令牌,并在表单提交时验证令牌,可以有效地防止跨站请求伪造(CSRF)攻击。
    在这里插入图片描述

基于Token的认证流程

优点

  1. 无状态和可扩展性:
  2. • 基于Token的认证是无状态的,服务器不需要存储Session信息,这使得系统更容易扩展,特别是在分布式系统中。
  3. 安全性:
    • Token通常经过数字签名,这确保了Token在传输过程中未被篡改。
    • 可以使用强大的加密算法来生成Token,增加了安全性。
  4. 支持跨域认证:
    • 由于Token是自包含的,它可以在多个域之间安全地传递,这使得它非常适合单点登录(SSO)场景。
  5. 自定义性强:
    • Token可以包含丰富的用户信息和权限数据,这些信息可以根据需要进行定制。
  6. 减少服务器负担:
    • 由于服务器不需要存储Session信息,这减少了服务器的存储和内存负担。
  7. 支持移动和分布式设备:
    • Token可以在多种环境中使用,包括移动设备和分布式系统中,这使得它非常适合现代的移动和云应用。
  8. 简单易于实现:
    • 基于Token的认证流程相对简单,易于实现和维护。
    在这里插入图片描述

Token详解 :

Token是一种令牌,它在计算机身份验证中用于代表用户身份或会话。在Web开发中,Token通常用于用户认证和授权,尤其是在无状态的API服务和单点登录(SSO)系统中。以下是对Token的详细解释:

Token的组成

以JWT(JSON Web Tokens)为例,一个Token通常由三部分组成,用点(.)分隔:

- Header(头部):

• 描述Token的元数据,例如Token的类型(JWT)和使用的签名算法(如HMAC SHA256或RSA)。

- Payload(负载):

• 包含声明(Claims),即有关实体(通常是用户)和其他数据的声明。

• 可以包含用户的角色、权限、Token的发行者、过期时间等信息。

- Signature(签名):

• 用于验证Token在传输过程中未被篡改。

• 通过使用头部指定的算法和密钥对头部和负载进行签名生成。

Token的安全性

数字签名:

• 使用私钥对Token进行签名,确保Token的完整性和真实性。

加密:

• 使用对称或非对称加密算法对Token进行加密,确保只有授权的接收者才能解密和读取Token内容。

过期时间:

• 设置Token的过期时间,过期后Token将失效,需要重新认证获取新的Token。

HTTPS:

• 在传输过程中使用HTTPS,防止Token在传输过程中被截获。

尚庭公寓后台管理系统登陆流程

在这里插入图片描述
根据上述图片我们可以分析出总共需要完成三个接口,以下是接口定义以及接口代码

获取图形验证码

导入maven依赖

<dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置application.yml

spring:data:redis:host: <hostname>port: <port>database: 0

controller

package com.nie.lease.web.admin.controller.login;import com.nie.lease.common.login.LoginUserHolder;
import com.nie.lease.common.result.Result;
import com.nie.lease.common.utils.JwtUtils;
import com.nie.lease.web.admin.service.LoginService;
import com.nie.lease.web.admin.vo.login.CaptchaVo;
import com.nie.lease.web.admin.vo.login.LoginVo;
import com.nie.lease.web.admin.vo.system.user.SystemUserInfoVo;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@Tag(name = "后台管理系统登录管理")
@RestController
@RequestMapping("/admin")
public class LoginController {@Autowiredprivate LoginService service;@Operation(summary = "获取图形验证码")@GetMapping("login/captcha")public Result<CaptchaVo> getCaptcha() {CaptchaVo result=service.getCaptcha();return Result.ok(result);}

service接口

package com.nie.lease.web.admin.service;import com.nie.lease.web.admin.vo.login.CaptchaVo;
import com.nie.lease.web.admin.vo.login.LoginVo;
import com.nie.lease.web.admin.vo.system.user.SystemUserInfoVo;public interface LoginService {CaptchaVo getCaptcha();}

service实现类

    @Overridepublic CaptchaVo getCaptcha() {SpecCaptcha specCaptcha = new SpecCaptcha(130,  48,  4);String code = specCaptcha.text().toLowerCase();String key = RedisConstant.ADMIN_LOGIN_PREFIX  + UUID.randomUUID();stringRedisTemplate.opsForValue().set(key,code,RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);return new CaptchaVo(specCaptcha.toBase64(),key);}

测试结果如下:
在这里插入图片描述
在这里插入图片描述

登录

导入maven依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId>
</dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><scope>runtime</scope>
</dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><scope>runtime</scope>
</dependency>
创建Jwt工具类
package com.nie.lease.common.utils;import com.nie.lease.common.exception.LeaseException;
import com.nie.lease.common.result.ResultCodeEnum;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;import javax.crypto.SecretKey;
import java.util.Date;public class JwtUtils {private static SecretKey tokenSignKey = Keys.hmacShaKeyFor("M0PKKI6pYGVWWfDZw90a0lTpGYX1d4AQ".getBytes());public static String createToken(Long userId, String username) {String token = Jwts.builder().setSubject("USER_INFO").setExpiration(new Date(System.currentTimeMillis() + 3600000)).claim("userId", userId).claim("username", username).signWith(tokenSignKey).compact();return token;}public static void main(String[] args) {System.out.println(createToken(2L, "user"));}public static Claims  parseToken(String token){if (token==null){throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);}try {JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(tokenSignKey).build();Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);return claimsJws.getBody();}catch (ExpiredJwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);}catch (JwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);}}
}
controller
    @Operation(summary = "登录")@PostMapping("login")public Result<String> login(@RequestBody LoginVo loginVo) {String token = service.login(loginVo);return Result.ok(token);}
service接口
    String login(LoginVo loginVo);
service实现类
    @Overridepublic String login(LoginVo loginVo) {//1.判断是否输入了验证码if (!StringUtils.hasText(loginVo.getCaptchaCode())) {throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);}//2.校验验证码String code = stringRedisTemplate.opsForValue().get(loginVo.getCaptchaKey());if (code == null) {throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);}if (!code.equals(loginVo.getCaptchaCode().toLowerCase())) {throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);}//3.校验用户是否存在SystemUser systemUser = systemUserMapper.selectOneByUsername(loginVo.getUsername());if (systemUser == null) {throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_NOT_EXIST_ERROR);}//4.校验用户是否被禁if (systemUser.getStatus() == BaseStatus.DISABLE) {throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_DISABLED_ERROR);}//5.校验用户密码if (!systemUser.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword()))) {throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_ERROR);}//6.创建并返回TOKENreturn JwtUtils.createToken(systemUser.getId(), systemUser.getUsername());}
mapper接口
SystemUser selectOneByUsername(String username);

mapperxml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nie.lease.web.admin.mapper.SystemUserMapper"><select id="pageSystemUser" resultType="com.nie.lease.web.admin.vo.system.user.SystemUserItemVo">select su.id,username,su.name,type,phone,avatar_url,additional_info,post_id,su.status,sp.name post_namefrom system_user suleft join system_post sp on su.post_id = sp.id and sp.is_deleted = 0<where>su.is_deleted = 0<if test="queryVo.name != null and queryVo.name != ''">and su.name like concat('%',#{queryVo.name},'%')</if><if test="queryVo.phone !=null and queryVo.phone != ''">and su.phone like concat('%',#{queryVo.phone},'%')</if></where></select><select id="selectOneByUsername" resultType="com.nie.lease.model.entity.SystemUser">select id,username,password,name,type,phone,avatar_url,additional_info,post_id,statusfrom system_userwhere is_deleted = 0and username = #{username}</select>
</mapper>
编写拦截器
书写解析token的方法
package com.nie.lease.common.utils;import com.nie.lease.common.exception.LeaseException;
import com.nie.lease.common.result.ResultCodeEnum;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;import javax.crypto.SecretKey;
import java.util.Date;public class JwtUtils {private static SecretKey tokenSignKey = Keys.hmacShaKeyFor("M0PKKI6pYGVWWfDZw90a0lTpGYX1d4AQ".getBytes());public static String createToken(Long userId, String username) {String token = Jwts.builder().setSubject("USER_INFO").setExpiration(new Date(System.currentTimeMillis() + 3600000)).claim("userId", userId).claim("username", username).signWith(tokenSignKey).compact();return token;}public static void main(String[] args) {System.out.println(createToken(2L, "user"));}public static Claims  parseToken(String token){if (token==null){throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);}try {JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(tokenSignKey).build();Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);return claimsJws.getBody();}catch (ExpiredJwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);}catch (JwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);}}
}

##### 编写拦截器

package com.nie.lease.web.admin.custom.interceptor;import com.nie.lease.common.exception.LeaseException;
import com.nie.lease.common.login.LoginUser;
import com.nie.lease.common.login.LoginUserHolder;
import com.nie.lease.common.result.ResultCodeEnum;
import com.nie.lease.common.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;@Component
public class AuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("access-token");Claims claims = JwtUtils.parseToken(token);Long userId = claims.get("userId", Long.class);String userName = claims.get("userName", String.class);LoginUserHolder.setLoginUser(new LoginUser(userId, userName));return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LoginUserHolder.clear();}
}
注册拦截器

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {@Autowiredprivate StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;@Autowiredprivate AuthenticationInterceptor authenticationInterceptor;@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverterFactory(this.stringToBaseEnumConverterFactory);}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(this.authenticationInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin/login/**");}}
http://www.lryc.cn/news/594547.html

相关文章:

  • Linux:线程控制
  • API获取及调用(以豆包为例实现图像分析)
  • 《计算机网络》实验报告三 UDP协议分析
  • 单线程 Reactor 模式
  • 【PyTorch】图像二分类项目
  • SSE和WebSocket区别到底是什么
  • 渗透笔记(XSS跨站脚本攻击)
  • `MYSQL`、`MYSQL_RES` 和 `MYSQL_FIELD`的含义与使用案例
  • [硬件电路-59]:电源:电子存储的仓库,电能的发生地,电场的动力场所
  • 2025最新 PostgreSQL17 安装及配置(Windows原生版)
  • BST(二叉搜索树)的笔试大题(C语言)
  • 【web安全】SQL注入与认证绕过
  • 【算法300题】:双指针
  • c#转python第四天:生态系统与常用库
  • XSS的介绍
  • Linux主机 ->多机器登录
  • 从零到精通:用DataBinding解锁MVVM的开发魔法
  • 【JS逆向基础】数据库之MongoDB
  • Django接口自动化平台实现(四)
  • SpringBoot的配置文件
  • 测试学习之——Pytest Day4
  • WPF学习笔记(28)Interaction.Triggers的意义与使用方式
  • 人工智能之数学基础:随机实验、样本空间、随机事件
  • 均值漂移累积监测算法(MDAM):原理、命名、用途及实现
  • 爬虫实战案例(两个)
  • 【Lua】大G表
  • Linux 基本指令详解
  • 【论文研读】SlowFast Networks for Video Recognition
  • 大语言模型调用方式与函数调用
  • 从磁记录到数据中心:磁盘原理与服务器架构的完整技术链路