Spring:强制登陆与拦截器

1.只使用session验证

(1)第一步:用户登陆时存储session

@ApiOperation("用户登陆")
@PostMapping("/login")
public AppResult login(HttpServletRequest request,
                           @RequestParam("username") @ApiParam("用户名") @NonNull String username,
                           @RequestParam("password") @ApiParam("用户密码") @NonNull String password) {
        //1.参数校验 -- 注解已完成
        //2.调用service层
        User user = userService.login(username, password);
        //3.设置session
        HttpSession session = request.getSession(true);
        session.setAttribute(AppConfig.USER_SESSION,user);
        return AppResult.success();
}

关键代码:

HttpServletRequest request
 //3.设置session
        HttpSession session = request.getSession(true);
        session.setAttribute(AppConfig.USER_SESSION,user);//存储的是key-value

 

(2)第二步:访问接口时验获取session

@ApiOperation("查询用户信息")
@GetMapping("/info")
public AppResult<User> getUserInfo(HttpServletRequest request) {
    log.info("获取用户信息");
    User user = null;
    HttpSession session = request.getSession(false);
    if(session == null || session.getAttribute(AppConfig.USER_SESSION) == null) {
        //用户未登录
       return AppResult.failed(ResultCode.FAILED_FORBIDDEN);
    }
    user = (User) session.getAttribute(AppConfig.USER_SESSION);
    }
    if(user == null) {
        return AppResult.failed(ResultCode.FAILED_FORBIDDEN);
    }
    return AppResult.success(user);
}

关键代码:

HttpServletRequest request//参数
HttpSession session = request.getSession(false);
if(session == null || session.getAttribute(AppConfig.USER_SESSION) == null) {
   //用户未登录
   return AppResult.failed(ResultCode.FAILED_FORBIDDEN);
}

只要获取不到session或者不能从session中获取到对象,就说明用户没有登陆。 


上述就是只使用session进行强制用户登陆的写法,需要在每个方法都进行判断。 

2.使用拦截器强制登录和session

上述是没有引入拦截器的场景,每个部分都需要引入相同的代码,就会使得代码非常的繁琐,所以我们就可以使用Spring统一功能中的拦截器。

简单介绍拦截器:由两个部分组成

第一个部分:定义拦截器(也就是指定拦截规则,比如没有session就不允许访问);第二个部分:配置拦截器,也就是执行这些规则(比如把保安投放到某某路口等)

(1)第一步:定义拦截器

语法:实现HandlerInterceptor接口,并重写其所有方法

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse    
response, Object handler) throws Exception {
        log.info("LoginInterceptor ⽬标⽅法执⾏前执⾏..");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("LoginInterceptor ⽬标⽅法执⾏后执⾏");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("LoginInterceptor 视图渲染完毕后执⾏,最后执⾏");
    }
}

其中里面有三个重写的方法,一般我们只需要重写第一个就够了,就是我们需要的拦截器

(1)preHeandle:目标方法前执行该方法(比如执行获取用户信息的方法前,会先执行该方法)。该方法返回true,后面的方法继续执行(不拦截);返回false,后面的方法中断。

(2)postHandle:目标方法执行后就会执行该方法。

(3)afterCompletion:视图渲染完毕后执行,最后执行(后端开发现在几乎不涉及视图,暂不了解)

定义拦截器:基本模版

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Slf4j
//把对象交给Spring管理,否则不生效
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取session
        HttpSession session = request.getSession(false);
        //2.判断是否存在session或者用户已登录
        if(session != null && session.getAttribute("user") != null) {
            //进入该方法,说明用户已登录,就不进行拦截操作
            return true;
        }
        //3.走到这一步,说明用户未登录,进行拦截(也可以指定跳转某一个页面)
        response.sendRedirect("/login.html");//设置跳转的页面,一般为登陆页面
        return false;//拦截并跳转
    }
}

上面就是配置拦截器的基本代码模版,有一个注意点:跳转的页面最好是封装成一个常量,利于后续的修改操作。

(2)第二步:配置拦截器

上面的第一步我们已经定义好了拦截规则,下面只需要配置就好(上面已经规定了哪些人员不准进入小区,接下来我们需要投放保安到小区的指定路口进行拦截)

语法:WebMvcConfigurer接口,并重写addInterceptors方法

基本模版:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppInterceptorConfigurer implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;//给保安们注入拦截规则

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)//该方法是注册,也就是执行拦截规则
                .addPathPatterns("/**")//该方法表示拦截所有的请求
                .excludePathPatterns("/login");//表示该页面不进行拦截
    }
}

上面就是模版,一般登录页面和注册等页面我们都是不进行拦截的,要是拦截住了就死锁了。

然后对于要拦截什么页面,看大家的需求;对于拦截器+session的介绍就结束了,也就是上面的两段代码。

3.使用令牌拦截

使用令牌技术生成一个token字符串用来验证用户是否登陆

下面是关于用户登陆和验证的基本步骤: 

 (1)前端发送登陆请求,后端进行验证

(2)后端验证通过,返回token

(3)客户端存储token(通过前端进行操作)

(4)客户端后续发起的请求,就会把token放在请求的header中(k-v的形式,token存储在v中)

(5)后端从header中获取到token(指定前端的k,才能获取token),进行验证

这个部分前面先介绍如何在代码中使用登陆令牌,后面再介绍代码的含义

使用步骤大致分为四步:引入依赖、编写令牌工作类、发放令牌、验证令牌

(1)第一步:引入依赖

引入pom依赖,需要使用到令牌专属的类

<!--  jwt令牌  -->
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
     <!-- or jjwt-gson if Gson is preferred -->
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

引入后记得刷新maven

(2)第二步:编写令牌工具utils

在主目录下创建utils包,再创建JwtUtils类

//负责生成令牌和验证令牌

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtUtils {


    private static long expiration =  30 * 60 * 1000;  //规定令牌有效期为30分钟
    private static String secretString = "gaiosfhioawjfajrawrawrawrawrawrawrawrfjarawrawrawrafawfoawjfa";
    private static Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));//key值,数字签名

    /**
     * 生成令牌
     */
    public static String genJwt(Map<String, Object> data) {
        //生产token
        String compact = Jwts.builder()//固定方法
                        .setClaims(data)//设置数据
                        .setExpiration(new Date(System.currentTimeMillis()+expiration))//设置token的过期时间
                        .signWith(key)//设置数字签名
                        .compact();//生产token
        return compact;
    }

    /**
     * 校验令牌
     */
    public static Claims verify(String token){
        //生成token用的都是同一个key,所以不需要传参
        //获取build对象
        JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();

        Claims claims = null;
        try {
            claims = build.parseClaimsJws(token).getBody();//与token校验
        }catch (ExpiredJwtException e){
            log.error("token过期, 校验失败, token:"+ token);
        }catch (Exception e){
            log.error("token校验失败, token:"+ token);
        }
        return claims;
    }

}

作用:这个类提供两个静态方法,第一个是负责生成令牌,第二个是负责验证令牌。

上述的生成和验证令牌属于大致模版,自己也可以进行大致的修改

(3)第三步:后端发放令牌

一般在登陆接口给用户发放令牌。

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {


    @RequestMapping("/login")
    public Result login(String userName, String password){
        //前面一堆验证工作,这里根据自己的业务功能写
        //………………
        UserInfo userInfo = new UserInfo();
        //账目密码验证完成,就可以给用户发放令牌了
        Map<String,Object> cliams = new HashMap<>();
        cliams.put("id", userInfo.getId());
        cliams.put("name", userInfo.getUserName());
        //获取对应的token
        String jwt = JwtUtils.genJwt(cliams);
        //返回token
        return Result.success(jwt);
    }
}

在验证完用户信息后,就可以给用户设置令牌并且返回token了,token中设置的信息一般不要设置隐私信息,比如用户密码

(4)第四步:后续验证令牌

这里直接采取统一拦截器进行拦截了,下面是关于拦截规则的代码

这一步作为纯后端程序猿来说比较困难,因为要和前端的参数进行同步。前端会把token(一个字符串)以k-v的形式放入header中,所以可以从请求中进行获取

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //验证token是否合法
        String userToken = request.getHeader("user_token_header");
        log.info("从header中获取信息, token:"+ userToken);
        Claims claims = JwtUtils.verify(userToken);
        if (claims==null){
            //token校验失败
            response.setStatus(401);
//            response.getOutputStream().write();
//            response.setContentType();
            return false;
        }
        return true;
    }
}

user_token_header是请求头中的key,用来获取对应的value值,也就是token

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码小娥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值