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
原文地址:https://blog.csdn.net/2301_77053417/article/details/142434262
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!