通过SpringBoot拦截器和JWT验证实现接口保护

一、使用场景

    单体应用的登录验证与接口保护:有时我们并不想希望我们的所有接口都是完全开放的,比如用户登陆后获取个人信息等接口,因此需要对用户进行验证,而验证过程需要在访问接口之前进行,因此可通过拦截器来方便的实现。(更复杂的认证授权可使用Shiro或SpringSecurity)

二、拦截器

    拦截器会在访问接口之前对请求进行拦截,因此可以在拦截器中对接口请求进行验证,只有通过认证才能对资源进行访问,不然直接返回。

三、JWT令牌认证

    拦截器对请求进行拦截了以后,就需要对用户进行认证,而JWT是当前使用最多的方式,相对于session来说减小了服务器的压力。JWT基于Json,令牌(token)中内容易扩展、数字签名防篡改、安全性高。地址:JWT官网
    实现流程: 用户通过用户名和密码登录以后,进行用户名和密码的比对,比对无误则根据用户名等(如id,TEL)信息生成token(生成协议为WT),返回给前端,前端将该token存储在Cookie或LocalStorage中,每次调用接口时都会将该token放到Header(请求头)中,用于后端的接口进行认证。
    简单理解:通过二维码(用户名和密码)去售票机(登录接口)取景区门票(token),然后拿着门票(token)去玩各个项目(资源接口),玩之前检票员(拦截器)会对票进行检验(令牌合法性的验证)。

四、JWT工具类

    既然需要这个令牌(token),那么就应有一套系统来生成、验证令牌(token)。而JWT这个工具类的目的就是用来生成令牌、验证令牌的合法性以及从令牌中获取令牌包含的信息。代码如下:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

public class JwtUtils {

    public static final long EXPIRE = 1000 * 60 * 60 * 12; // token过期时间  12小时
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; // 加密的密钥

    // 生成token字符串
    public static String getJwtToken(String id, String nickname){

        String JwtToken = Jwts.builder()
                // 头信息
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256") // 加密方式
                // 设置过期时间
                .setSubject("xxx") // 项目名
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))

                // 设置用户信息 可以加多个
                .claim("id", id)
                .claim("nickname", nickname)
                // 签名方式
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();
        return JwtToken;
    }

    // 判断token的合法性、有效期等进行判断,直接对token进行判断
    // 注意这里的异常直接交给拦截器中去处理
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            throw e; // 抛出异常交给拦截器处理
        }
        return true;
    }

	// 判断token是否存在与有效,从请求头中获取token
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

	// 根据token获取用户信息
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody(); // 得到用户数据的主体
        return (String)claims.get("id");
    }

	// 根据token获取用户信息 从请求头中获取token
    public static String getMemberNickNameByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody(); // 得到用户数据的主体
        return (String)claims.get("nickname");
    }
}

五、颁发令牌

    这里需要做的就是用户登陆并验证通过后对用户颁发一个令牌(token),这样用户再来访问接口的时候就带着这个令牌来访问。

@PostMapping("login")
    public DataReturn login(@RequestBody User user) {
        // 1. 验证用户名和密码
		// 2. 验证通过则通过工具类中的getJwtToken方法生成token并返回
    }

六、请求拦截

    使用拦截器也非常的简单,只需要实现在拦截器类与拦截器的配置类中实现HandlerInterceptorWebMvcConfigurer接口,并进行相应的配置。(这两个类名可随意取,只要实现这两个接口即可)

① 拦截器类:JwtInterception.class
    注意1:这里通过了所有的OPTION请求,因为浏览器在发送请求之前会预发送一个OPTION,不过滤掉虽然可以访问到结果,但是会生成大量错误日志。同时这里对验证token产生的异常时对不同的异常进行了处理,如无效、过期等,并返回对应的信息到前端。
    注意2:这里直接通过request.getHeader("token") 获取token,因此前端在请求头中放token时不需要使用Authorization Bearer +token的方式,直接token:token值 即可,当然可根据自己实际情况修改。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.xxxxutils.JwtUtils;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

public class JwtInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(request.getMethod().toUpperCase().equals("OPTIONS")){
            return true; // 通过所有OPTION请求
        } else {
            String token = request.getHeader("token"); // 获取请求头中的token
            Map<String, Object> map = new HashMap<>();
            try {
                boolean verify = JwtUtils.checkToken(token);
                if (verify) {
                    return true; // 通过验证
                } else {
                    return false; // 未通过验证
                }
            } catch (SignatureException e) {
                e.printStackTrace();
                map.put("msg", "无效签名");
            } catch (UnsupportedJwtException e) {
                e.printStackTrace();
                map.put("msg", "不支持的签名");
            } catch (ExpiredJwtException e) {
                e.printStackTrace();
                map.put("msg", "token过期");
            } catch (MalformedJwtException e) { // IllegalArgumentException
                e.printStackTrace();
                map.put("msg", "不支持的签名格式");
            } catch (Exception e) {
                e.printStackTrace();
                map.put("msg", "token无效");
            }
            map.put("state", false);
            // 将map转为json
            String json = new ObjectMapper().writeValueAsString(map);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(json);
            return false;
        }
    }
}

② 拦截器配置类:InterceptionConfig.class
    只需要实现WebMvcConfigurer接口并进行拦截器的配置,包括对什么样的请求进行拦截,对什么样的请求不拦截。

import com.ziyue.courseservice.interceptors.JwtInterceptor;
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 InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JwtInterceptor())
                .addPathPatterns("/service/**") // 拦截的请求 /service/**   表示拦截service下所有
                .excludePathPatterns("/user/login"); // 不拦截的请求  如登录接口
    }
}



  

Email : beyonderwei@Gmail.com
Website : http://beyonderwei.com

WeChat:
在这里插入图片描述

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
下面是一个使用 Spring Boot、JWT拦截器实现登录验证的示例代码: 1. 添加 JWT 依赖 在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> ``` 2. 创建 JWT 工具类 ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import org.springframework.stereotype.Component; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JwtUtil { private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); public String generateToken(String subject) { return Jwts.builder() .setClaims(new HashMap<>()) .setSubject(subject) .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) .signWith(key) .compact(); } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); return true; } catch (Exception e) { return false; } } public String getUsernameFromToken(String token) { Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); return claims.getSubject(); } } ``` 3. 创建拦截器 ```java import com.example.demo.jwt.JwtUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; @Component public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired private JwtUtil jwtUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token != null && jwtUtil.validateToken(token)) { String username = jwtUtil.getUsernameFromToken(token); request.setAttribute("username", username); return true; } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); LoginRequired loginRequired = method.getAnnotation(LoginRequired.class); if (loginRequired != null && modelAndView != null) { modelAndView.addObject("username", request.getAttribute("username")); } } } ``` 4. 创建注解 ```java import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LoginRequired { } ``` 5. 创建控制器 ```java import com.example.demo.interceptor.LoginRequired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/") public String index() { return "Hello World"; } @GetMapping("/hello") @LoginRequired public String hello() { return "Hello " + SecurityContextHolder.getContext().getAuthentication().getName(); } } ``` 6. 配置拦截器 ```java import com.example.demo.interceptor.AuthenticationInterceptor; 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 WebMvcConfig implements WebMvcConfigurer { @Autowired private AuthenticationInterceptor authenticationInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor).addPathPatterns("/**"); } } ``` 7. 创建登录控制器 ```java import com.example.demo.jwt.JwtUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController public class LoginController { @Autowired private JwtUtil jwtUtil; @PostMapping("/login") public Map<String, Object> login(@RequestBody Map<String, String> params) { String username = params.get("username"); String password = params.get("password"); // TODO: 验证用户名和密码 String token = jwtUtil.generateToken(username); Map<String, Object> result = new HashMap<>(); result.put("token", token); return result; } } ``` 在这个示例中,我们创建了一个 JWT 工具类来生成和验证 JWT,创建了一个拦截器验证用户是否登录,并使用 @LoginRequired 注解来标记需要登录验证的方法。我们还创建了一个登录控制器来生成 JWT。通过这个示例,您可以了解如何使用 Spring Boot、JWT拦截器实现登录验证

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值