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

登陆认证

基于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依赖

xml 复制代码
<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

xml 复制代码
spring:
  data:
    redis:
      host: <hostname>
      port: <port>
      database: 0

controller

java 复制代码
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 {

    @Autowired
    private LoginService service;

    @Operation(summary = "获取图形验证码")
    @GetMapping("login/captcha")
    public Result<CaptchaVo> getCaptcha() {
        CaptchaVo result=service.getCaptcha();
        return Result.ok(result);
    }

service接口

java 复制代码
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实现类

java 复制代码
    @Override
    public 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依赖
xml 复制代码
<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工具类
java 复制代码
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
java 复制代码
    @Operation(summary = "登录")
    @PostMapping("login")
    public Result<String> login(@RequestBody LoginVo loginVo) {
        String token = service.login(loginVo);
        return Result.ok(token);
    }
service接口
java 复制代码
    String login(LoginVo loginVo);
service实现类
java 复制代码
    @Override
    public 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.创建并返回TOKEN
        return JwtUtils.createToken(systemUser.getId(), systemUser.getUsername());
    }
mapper接口
java 复制代码
SystemUser selectOneByUsername(String username);

mapperxml文件

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//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_name
        from system_user su
                 left 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,
               status
        from system_user
        where is_deleted = 0
          and username = #{username}
    </select>
</mapper>
编写拦截器
书写解析token的方法
java 复制代码
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);
        }

    }
}

##### 编写拦截器

java 复制代码
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 {

    @Override
    public 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;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LoginUserHolder.clear();
    }
}
注册拦截器
java 复制代码
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Autowired
    private StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;

    @Autowired
    private AuthenticationInterceptor authenticationInterceptor;
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(this.stringToBaseEnumConverterFactory);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.authenticationInterceptor)
                .addPathPatterns("/admin/**")
                .excludePathPatterns("/admin/login/**");
    }



}
相关推荐
ZC1111K3 小时前
maven(配置)
java·maven
@zcc@4 小时前
Maven 依赖管理
maven
慕y2745 小时前
Java学习第五十八部分——设计模式
java·学习·设计模式
躲在云朵里`5 小时前
SpringBoot的介绍和项目搭建
java·spring boot·后端
老马啸西风5 小时前
windows docker-02-docker 最常用的命令汇总
linux·运维·ubuntu·docker·容器·eureka·maven
菜还不练就废了5 小时前
7.19-7.20 Java基础 | File类 I/O流学习笔记
java·笔记·学习
Yweir5 小时前
Elastic Search 8.x 分片和常见性能优化
java·python·elasticsearch
心平愈三千疾6 小时前
学习秒杀系统-页面优化技术
java·学习·面试
程序员JerrySUN7 小时前
Valgrind Memcheck 全解析教程:6个程序说明基础内存错误
android·java·linux·运维·开发语言·学习