今天就来完成登入验证的部分!
昨天已经完成发送帐号密码到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出现。
说到私钥,正常来讲应该要把它放在资料库中才是符合资安规范的做法
之後有空再改为从资料库读取参数
接下来就是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
带上jwt token时,回200,看来ok
当jwt过期也会被阻挡:
说来惭愧,这样写下来发现其实根本没什麽没用到Spring Security的功能
未来有机会再看看有什麽地方可以用到
今天就先这样啦~
>>: 2.4.1 Design System - Avatar元件
前言 今天要在 RecipeDetailView 中添加 Picker controller, 使其...
继 higher-order-component 後,今天来说明 react 中另个概念 rende...
环境准备 使用 Cloudflare DNS 安装 LiteSpeed Cache plugin ...
412. FizzBuzz 412. FizzBuzz 是一个相当经典的题目,号称是 Google...
前言 今天先介绍一下JavaScript的一些背景, 再说一下JavaScript的变数与资料类型。...