JWT全称JSON Web Token,实现过程简单的说就是用户登录成功之后,将用户的信息进行加密,然后生成一个token返回给客户端,与传统的session交互没太大区别。
区别就是token存放了用户的基本信息,更直观一点就是将原本放入redis中的用户数据,放入到token中去了。
io.jsonwebtoken
jjwt
0.9.0
public static String getBearerToken(String userId, Integer expireSecond) {
Date date = new Date(System.currentTimeMillis() + expireSecond * 1000);
//json web token构建
return Jwts.builder()
//此处的subject可以用一个用户名,也可以是多个信息的组合,根据需要来定
.setSubject(userId)
//设置token过期时间,24小時
.setExpiration(date)
//设置token签名、密钥
.signWith(SignatureAlgorithm.HS512, jwtKey)
.compact();
}
public static String parseBearerToken(String token) {
token = token.replace("Bearer ", "");
return Jwts.parser()
//签名、密钥
.setSigningKey(jwtKey)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
@Configuration
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//关闭跨站请求防护
.cors().and().csrf().disable()
.authorizeRequests()
//需要授权后访问
.anyRequest().authenticated()
.and()
//增加登录拦截
.addFilter(new JWTLoginFilter(authenticationManager()))
//增加是否登陸过滤
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
//前后端分离是无状态的,所以暂时不用session,將登录信息保存在token中。
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(WebSecurity web) {
//允许不登录就可以访问的方法,多个用逗号分隔
web.ignoring().antMatchers("/api/demo/**","/api/test/test");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
public UserDetailsService userDetailsService() {
//校验用户
return username -> {
UserEntity user = userService.getUserByName(username);
if (user == null) {
log.error("loadUserByUsername user not found {}", username);
return null;
}
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), emptyList());
};
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
/**
* 对请求进行过滤
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
try {
log.info("doFilterInternal"+request.getRequestURI());
//请求体的头中是否包含Authorization
String header = request.getHeader("Authorization");
//Authorization中是否包含Bearer,有一个不包含时直接返回
if (header == null || !header.startsWith("Bearer ")) {
responseJson(response);
return;
}
//获取权限失败,会抛出异常
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
//获取后,将Authentication写入SecurityContextHolder中供后序使用
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
} catch (Exception e) {
log.info("doFilterInternal"+e.toString());
responseJson(response);
e.printStackTrace();
}
}
/**
* 未登录时的提示
*/
private void responseJson(HttpServletResponse response){
try {
//未登录时,使用json進行提示
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = response.getWriter();
Map map = new HashMap();
map.put("code", HttpServletResponse.SC_FORBIDDEN);
map.put("message","请登录!");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 通过token,获取用户信息
*/
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (token != null) {
//通过token解析出用户信息
String user = JwtUtil.parseBearerToken(token);
//不为null,返回
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
@Slf4j
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTLoginFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* 接收并解析用户凭证,出現错误时,返回json数据前端
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res){
try {
UserEntity user = new ObjectMapper().readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
user.getUserName(),
user.getPassword(),
new ArrayList<>())
);
} catch (Exception e) {
try {
//未登录提示账号密码错误
res.setContentType("application/json;charset=utf-8");
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter out = res.getWriter();
Map map = new HashMap();
map.put("code",HttpServletResponse.SC_UNAUTHORIZED);
map.put("message","账号或密码错误!");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
} catch (Exception e1) {
e1.printStackTrace();
}
throw new RuntimeException(e);
}
}
/**
* 用户登录成功后,生成token,并且返回json数据给前端
*/
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth){
String userName = ((UserDetails) auth.getPrincipal()).getUsername();
String token = JwtUtil.getBearerToken(userName, 24*3600);
//返回token
res.addHeader("Authorization", "Bearer " + token);
try {
//登录成功時,返回json格式进行提示
res.setContentType("application/json;charset=utf-8");
res.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = res.getWriter();
Map map = new HashMap();
map.put("code",HttpServletResponse.SC_OK);
map.put("message","登陆成功!");
map.put("token","Bearer " + token);
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}