secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
JWT在spring boot中的使用:
首先要添加JWT的依赖:
然后在配置文件中添加JWT的配置信息:
audience:
clientId: 098f6bcd4621d373cade4e832627b4f6
base64Secret: MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=
name: restapiuser
expiresSecond: 172800
配置信息的实体类,以便获取jwt的配置:在config下面
@Data
@ConfigurationProperties(prefix = "audience")
@Component
public class Audience {
private String clientId;
private String base64Secret;
private String name;
private int expiresSecond;
}
添加拦截器:
public class JwtFilter extends GenericFilterBean {
@Autowired
private Audience audience;
/**
* Reserved claims(保留),它的含义就像是编程语言的保留字一样,属于JWT标准里面规定的一些claim。JWT标准里面定好的claim有:
iss(Issuser):代表这个JWT的签发主体;
sub(Subject):代表这个JWT的主体,即它的所有人;
aud(Audience):代表这个JWT的接收对象;
exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
jti(JWT ID):是JWT的唯一标识。
* @param req
* @param res
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
//等到请求头信息authorization信息
final String authHeader = request.getHeader("authorization");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
chain.doFilter(req, res);
} else {
if (authHeader == null || !authHeader.startsWith("bearer;")) {
throw new LoginException(ResultEnum.LOGIN_ERROR);
}
final String token = authHeader.substring(7);
try {
if(audience == null){
BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
audience = (Audience) factory.getBean("audience");
}
final Claims claims = JwtHelper.parseJWT(token,audience.getBase64Secret());
if(claims == null){
throw new LoginException(ResultEnum.LOGIN_ERROR);
}
request.setAttribute(Constants.CLAIMS, claims);
} catch (final Exception e) {
throw new LoginException(ResultEnum.LOGIN_ERROR);
}
chain.doFilter(req, res);
}
}
}
注册JWT拦截器,可以在配置类中,也可以直接在SpringBoot的入口类中:
}
编写登录Controller:
@RestController
@RequestMapping("/user")
public class LoginController {
// @Autowired
// private UserService userService;
@ApiOperation(value="用户登录", notes="使用JWT实现用户登录demo")
@GetMapping("/login")
public Object login(String userName,String passpword) throws ServletException{
if (userName == "" || userName == null || passpword == "" || passpword == null)
throw new ServletException("Please fill in username and password");
//这里不仅要判断前台传来的用户名和密码是否为空,而且要到数据库校验,判断是否注册过此用户,这里省略
//创建 JWT token
String jwtToken = Jwts.builder().setSubject(userName).claim("roles", "member").setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact();
return jwtToken;
}
}
我们调用loginController中的login方法会返回一个token,值为:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsaWp1YW4iLCJyb2xlcyI6Im1lbWJlciIsImlhdCI6MTUyNzU1NzU2MH0.TcthyjCcJCR4ciD5F-Ps_3WbLmgpN8l1t-3rewv05TA
这就是JWT登录成功后返回给客户端的秘钥token
我们再新建一个SecureController判断是否登录成功,
@RestController
@RequestMapping("/secure")
public class SecureController {
@RequestMapping("/users/user")
public String loginSuccess() {
return "Login Successful!";
}
}
使用postman调用SecureController中的 loginSuccess方法时,会报错:
{ "timestamp": 1527560046368,
"status": 500,
"error": "Internal Server Error",
"exception": "javax.servlet.ServletException",
"message": "Missing or invalid Authorization header",
"path": "/secure/users/user"
}
这是因为请求此接口的时候,没有带上登录成功后生成的token值
我们在Authorization中选择TYPE为Breaer Token ,并将登录返回给客户端的token放在此Token中,再请求此验证是否登录成功的接口,此时会返回:"Login Successful!"
至此,JWT入门demo已结束。