[Day 10] - Spring Boot 实作登入验证(四)(JWT登入验证)

今天就来完成登入验证的部分!

昨天已经完成发送帐号密码到api,验证ok即发送一笔JWT给client,
接下来要实作的部分就是要让除了登入url(/api/v1/user/login)以外的url
都需先验证是否带有正确的JWT,否则不放行。

为了做到以上功能,需要实作filter,这边继承OncePerRequestFilter,确保request只会被filter过滤一次

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.web.filter.OncePerRequestFilter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

public class AuthorizationCheckFilter extends OncePerRequestFilter{

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
                //如果不是登入就拦截
                if(!req.getServletPath().equals("/api/v1/user/login")){
                    String authorHeader =  req.getHeader(AUTHORIZATION);
                    String bearer ="Bearer ";
                    //以jjwt验证token,只要验证成功就放行
                    //验证失败会抛exception,直接将错误讯息传回
                    if(authorHeader!= null && authorHeader.startsWith(bearer)){
                        try{
                        String token = authorHeader.substring(bearer.length());
                        Claims claims = Jwts.parser().setSigningKey("MySecret")
                        .parseClaimsJws(token).getBody();

                        System.out.println("JWT payload:"+claims.toString());

                        chain.doFilter(req, res);
                        
                        }catch(Exception e){
                            System.err.println("Error : "+e);
                            res.setStatus(FORBIDDEN.value());
                            
                            Map<String, String> err = new HashMap<>();
                            err.put("jwt_err", e.getMessage());
                            res.setContentType(APPLICATION_JSON_VALUE);
                            new ObjectMapper().writeValue(res.getOutputStream(), err);
                        }
                    }else{
                        res.setStatus(UNAUTHORIZED.value());
                    }
                }else{
                    chain.doFilter(req, res);
                }
        
    }

}

这里我以最符合JWT精神的方式来做验证,只要验证传来的token确实是我发出的,就直接放行,
查资料时看有人分享的实作范例除了验证jwt之外,还会取出sub来跟资料库做比对,确认是不是真的有这个用户,个人觉得算是比较严谨的作法。因为就是已经验证过了,才会发出jwt给客户,除非我们的私钥被盗,否则不可能会有不存在的客户的jwt出现。

说到私钥,正常来讲应该要把它放在资料库中才是符合资安规范的做法
之後有空再改为从资料库读取参数/images/emoticon/emoticon25.gif

接下来就是Spring Security

我在[Day 06] - 用Spring Boot 建立Controller建立了WebSecurityConfig,java

将刚刚写好的filter加进来:

import com.rei1997.vault.util.filter.AuthorizationCheckFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;


@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关掉csrf保护
        http.csrf().disable();
        //不写session了
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //加上刚刚写的filter
        http.addFilterBefore(new AuthorizationCheckFilter(), BasicAuthenticationFilter.class);
    }  

}

就这样

来验证一下,我在Controller写了一个简单的getMapping的方法,会回传User的资讯

当不带Authorization header时,会回传401,表示有成功挡下来未授权的request
https://ithelp.ithome.com.tw/upload/images/20210925/20128973cibqP13WeT.png

带上jwt token时,回200,看来ok
https://ithelp.ithome.com.tw/upload/images/20210925/201289732HZLTGeKsy.png

当jwt过期也会被阻挡:
https://ithelp.ithome.com.tw/upload/images/20210925/20128973qaDhekvFiU.png

说来惭愧,这样写下来发现其实根本没什麽没用到Spring Security的功能/images/emoticon/emoticon20.gif
未来有机会再看看有什麽地方可以用到
今天就先这样啦~


<<:  [Day15] 碰撞侦测 - 分离轴原理 SAT

>>:  2.4.1 Design System - Avatar元件

Day29: Picker controller

前言 今天要在 RecipeDetailView 中添加 Picker controller, 使其...

react 大冒险-render props-day 23

继 higher-order-component 後,今天来说明 react 中另个概念 rende...

QUIC.cloud CDN 与 CloudFlare 新手教学

环境准备 使用 Cloudflare DNS 安装 LiteSpeed Cache plugin ...

LeetCode 双刀流: 412. FizzBuzz

412. FizzBuzz 412. FizzBuzz 是一个相当经典的题目,号称是 Google...

Day 10 - JavaScript(1) : 变数与资料类型

前言 今天先介绍一下JavaScript的一些背景, 再说一下JavaScript的变数与资料类型。...