欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前后端分离之Springboot后端

程序员文章站 2024-01-25 22:11:28
...

这是上一篇博客前后端分离之Java后端的重写.
源码
前后端分离的后端主要解决的就2个问题 : 跨域访问(CORS)token校验,下面快速说明.

1.项目环境

使用Intellij IDE.
项目结构:
前后端分离之Springboot后端

2.跨域访问

解决跨域很简单,翻一下官方文档很容易解决,我们就使用全局的通过注解实现的方式:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //允许全部请求跨域
        registry.addMapping("/**");
    }
}

3.Token验证

这节分为2个部分,一是原理,二是代码实现.

3.1 原理

在第一篇文章里,我是这样说的:

在用户第一次登录成功后,服务端返回一个token回来,这个token是根据userId进行加密的,**只有服务器知道,然后浏览器每次请求都把这个token放在Header里请求,这样服务器只需进行简单的解密就知道是哪个用户了。

3.2 代码实现

避免重复造*,我们依然使用JWT,这个标准在2015年提出,查看RFC文档,它的一个实现JJWT

        <!-- JJWT -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

我们要做的很简单 :登录时生成Token,拦截每次请求检查token.

3.2.1 生成Token与验证

详情查看代码注释

public class JwtUtil {
    final static String base64EncodedSecretKey = "base64EncodedSecretKey";//私钥
    final static long TOKEN_EXP = 1000 * 60;//过期时间,测试使用60秒

    public static String getToken(String userName) {
        return Jwts.builder()
                .setSubject(userName)
                .claim("roles", "user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + TOKEN_EXP)) /*过期时间*/
                .signWith(SignatureAlgorithm.HS256, base64EncodedSecretKey)
                .compact();
    }

    /**
     * @Date:17-12-12 下午6:21
     * @Author:root
     * @Desc:检查token,只要不正确就会抛出异常
     **/
    public static void checkToken(String token) throws ServletException {
        try {
            final Claims claims = Jwts.parser().setSigningKey(base64EncodedSecretKey).parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e1) {
            throw new ServletException("token expired");
        } catch (Exception e) {
            throw new ServletException("other token exception");
        }
    }
}

3.2.2 拦截token

在spring里很好实现全局拦截,过滤器,拦截器,AOP都可以实现.
因为filter是对资源过滤,我们这里没有资源了,只有URL,而AOP着重处理过程.综合考虑,这里选择拦截器比较合适.
我们的拦截器:先检查header,取出token,验证.

public class JwtInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new ServletException("invalid Authorization header");
        }
        //取得token
        String token = authHeader.substring(7);
        try {
            JwtUtil.checkToken(token);
            return true;
        } catch (Exception e) {
            throw new ServletException(e.getMessage());
        }
    }
}

注册拦截器:将登录排除

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //允许全部请求跨域
        registry.addMapping("/**");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        registry.addInterceptor(new JwtInterceptor()).excludePathPatterns("/user/login");
    }
}

全局异常处理:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        return "err:" + e.getMessage();
    }
}

登录:

    @PostMapping("/login")
    public String login(User user) throws ServletException {
        String name = user.getUsername();
        String pass = user.getPassword();
        if (!"admin".equals(name)) {
            throw new ServletException("no such user");
        }
        if (!"1234".equals(pass)) {
            throw new ServletException("wrong password");
        }
        return JwtUtil.getToken(name);
    }

其他根据需要可以查看源码.

当然,整个系统我没有使用RESTful的统一API,可以自定义一个类去处理,这里不重要.

4.如何请求

将得到的token封装在header里,如下:
前后端分离之Springboot后端

这种请求放在Axios这样的请求框架很好实现,特别是在React或Vue里.