【项目实践】公寓租赁项目(十):基于SpringBoot登录管理接口开发

【项目实践】公寓租赁项目(十):基于SpringBoot登录管理接口开发

目录

登录接口说明:LoginController

一、获取图形验证码接口

controller层:

service层:

二、登录接口

controller层:

service层:

mapper层:

三、JwtUtil创建

四、获取登陆用户个人信息接口

controller层:

(重要)因为我们解析token的方法是在拦截器里调用,所以如果我们每一次获取登录者的时候,要获取token进行解析,连续调用两次parseToken()方法就不太合理了。所以为避免重复解析,通常会在拦截器将Token解析完毕后,将结果保存至ThreadLocal中,这样一来,我们便可以在整个请求的处理流程中进行访问了。

修改拦截器:

service层:


登录接口说明:LoginController

@Tag(name = "后台管理系统登录管理")
@RestController
@RequestMapping("/admin")
public class LoginController {

    @Autowired
    private LoginService service;
}

一、获取图形验证码接口

接口名称:getCaptcha

请求方式:Get

请求路径:/admin/login/captcha

请求参数:无

返回类型:CaptchaVo

我们先查看CaptchaVo有哪些属性:

@Data
@Schema(description = "图像验证码")
@AllArgsConstructor
public class CaptchaVo {

    @Schema(description="验证码图片信息")
    private String image;

    @Schema(description="验证码key")
    private String key;
}

需要获取验证码图片的信息和存储在redis里的key。那么怎么生成验证码呢?我们需要使用开源的验证码生成工具EasyCaptcha。

导入maven依赖:

<dependency>
    <groupId>***.github.whvcse</groupId>
    <artifactId>easy-captcha</artifactId>
</dependency>

controller层:

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

service层:

public CaptchaVo getCaptcha() {
        Spe***aptcha spe***aptcha = new Spe***aptcha(130, 48, 4);  //分别设置验证码区域的长、宽、以及验证码长度。
        String code = spe***aptcha.text().toLowerCase();  //把验证码转成小写
        String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID(); //遵循命名规范

        stringRedisTemplate.opsForValue().set(key,code,RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);  //把key和验证码值存入redis
        return new CaptchaVo(spe***aptcha.toBase64(),key);
    }

本项目Reids中的key需遵循以下命名规范:项目名:功能模块名:其他,例如admin:login:123456


二、登录接口

接口名称:login

请求方式:Post

请求路径:/admin/login

请求参数:@RequestBody LoginVo loginVo

返回类型:Result<String>

controller层:

@Operation(summary = "登录")
    @PostMapping("login")
    public Result<String> login(@RequestBody LoginVo loginVo) {
        String jwt=service.login(loginVo);
        return Result.ok(jwt);
    }

loginVo用来封装前端登录界面用户提交的用户名、密码、验证码以及验证码在redis的key

@Data
@Schema(description = "后台管理系统登录信息")
public class LoginVo {

    @Schema(description="用户名")
    private String username;

    @Schema(description="密码")
    private String password;

    @Schema(description="验证码key")
    private String captchaKey;

    @Schema(description="验证码code")
    private String captchaCode;
}

service层:

public String login(LoginVo loginVo) {
        if (loginVo.getCaptchaCode()==null){  //判断输入的验证码是否为空
            throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);
        }

        String code = stringRedisTemplate.opsForValue().get(loginVo.getCaptchaKey());  //从redis中获取code
        if (code==null){  //判断查询的code是否为空
            throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);
        }
        if (!code.equals(loginVo.getCaptchaCode().toLowerCase())){  //如果code与输入的code的小写相等
            throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);
        }
        SystemUser systemUser = systemUserMapper.selectOneByUsername(loginVo.getUsername());  //通过用户名查询用户
        if (systemUser==null){
            throw new LeaseException(ResultCodeEnum.ADMIN_A***OUNT_NOT_EXIST_ERROR);
        }
        if (systemUser.getStatus()== BaseStatus.DISABLE){
            throw new LeaseException(ResultCodeEnum.ADMIN_A***OUNT_DISABLED_ERROR);
        }
        if (!systemUser.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword()))){
            throw new LeaseException(ResultCodeEnum.ADMIN_A***OUNT_ERROR);
        }
        return JwtUtil.createToken(systemUser.getId(),systemUser.getUsername());  //返回token
    }

mapper层:

自定义通过用户名字查询用户的方法。

 <select id="selectOneByUsername" resultType="***.atguigu.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>

三、JwtUtil创建

JwtUtil是用来生成token来识别登录的用户是否合法,如果合法则从数据库中查询用户信息,并响应给前端。而且后续前端想要请求后端接口也需要解析携带的token。

导入依赖:

<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>
public class JwtUtil {

    private static long tokenExpiration = 60 * 60 * 1000L;
    private static SecretKey tokenSignKey = Keys.hmacShaKeyFor("M0PKKI6pYGVWWfDZw90a0lTpGYX1d4AQ".getBytes());

    /**
     * 创建token方法
     * @param userId
     * @param username
     * @return
     */
    public static String createToken(Long userId, String username) {
        String token = Jwts.builder().
                setSubject("LOGIN_USER").
                setExpiration(new Date(System.currentTimeMillis() + 3600000)).
                claim("userId", userId).
                claim("username", username).
                signWith(tokenSignKey, SignatureAlgorithm.HS256).
                ***pact();
        return token;
    }

    /**
     * 解析token方法
     * @param token
     * @return
     */
    public static Claims parseToken(String token){
        if (token==null){
            throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);
        }

        try {
            JwtParser jwtParser = Jwts.parser().setSigningKey(tokenSignKey)
                    .build();
            Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);
            Claims claims = claimsJws.getBody();
            return claims;
        }catch (ExpiredJwtException e){
            throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);
        }catch (JwtException e){
            throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);
        }

    }

四、获取登陆用户个人信息接口

接口名称:info

请求方式:Get

请求路径:/admin/info

请求参数:无

返回类型:Result<SystemUserInfoVo>

controller层:

返回的类型为VO对象,需要自己定义查询方法。

重要)因为我们解析token的方法是在拦截器里调用,所以如果我们每一次获取登录者的时候,要获取token进行解析,连续调用两次parseToken()方法就不太合理了。所以为避免重复解析,通常会在拦截器将Token解析完毕后,将结果保存至ThreadLocal中,这样一来,我们便可以在整个请求的处理流程中进行访问了。

ThreadLocal的主要作用是为每个使用它的线程提供一个独立的变量副本,使每个线程都可以操作自己的变量,而不会互相干扰,其用法如下图所示。

每个ThreadLocal对象都拥有set、get、remove方法,我们只需要编写逻辑调用即可自定义存储、获取、清理本地线程对象的方法。

public class LoginUserHolder {
    public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();  //创建本地线程对象

    public static void setLoginUser(LoginUser loginUser) {
        threadLocal.set(loginUser);   //把登录用户信息存储进本地线程对象
    }

    public static LoginUser getLoginUser() {
        return threadLocal.get();  //获取登录用户信息
    }

    public static void clear() {
        threadLocal.remove();  //清理存储的用户信息
    }
}
@Operation(summary = "获取登录用户个人信息")
    @GetMapping("info")
    public Result<SystemUserInfoVo> info() {
        Long userId = LoginUserHolder.getLoginUser().getUserId();
        SystemUserInfoVo systemUserInfoVo=service.getLoginUserInfoById(userId);
        return Result.ok(systemUserInfoVo);
    }

修改拦截器:

@***ponent
public class AuthenticationIntercepter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("a***ess-token");
        Claims claims = JwtUtil.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 after***pletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LoginUserHolder.clear();  //清理本地线程方法
    }
}

service层:

public SystemUserInfoVo getLoginUserInfoById(Long userId) {
        SystemUser systemUser = systemUserMapper.selectById(userId);
        SystemUserInfoVo systemUserInfoVo = new SystemUserInfoVo();
        systemUserInfoVo.setAvatarUrl(systemUser.getAvatarUrl());
        systemUserInfoVo.setName(systemUser.getName());
        return systemUserInfoVo;
    }

转载请说明出处内容投诉
CSS教程网 » 【项目实践】公寓租赁项目(十):基于SpringBoot登录管理接口开发

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买