上一篇 <<<安全框架--SpringSecurity
下一篇 >>>安全框架--OAuth2
JWT(JSON WEB Token): JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT的组成
- 第一部分:header (头部)
{
Typ=”jwt” ---类型为jwt
Alg:”HS256” --加密算法为hs256,用于验签
}
- 第二部分:playload(载荷data)
携带存放的数据 用户名称、用户头像之类 注意铭感数据
标准中注册的声明 (建议但不强制使用) :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
- 第三部分:secret (存放在服务器端)
Base64(header.playload) +秘钥
JWT和传统token的区别是什么
jwt的值是直接在token中,存储在客户端的
传统token的值是存在redis或数据库中的,返回给客户端的是key
JWT的应用场景
前端分离项目:(移动app、小程序、H5)
JWT的优缺点
优点:
a.无需再服务器存放用户的数据,减去服务器端压力
b.轻量级、json风格比较简单
c.跨语言
缺点:
Token一旦生成后期无法修改:
a.无法更新token有效期和数据
b.无法销毁一个token
JWT是否安全
安全机制肯定是有的,就算黑客想篡改payload中的权限列表,必须获取到服务器端秘钥,自己生成jwt才可以。
JWT如何绑定userId等敏感数据
对敏感数据实现对称加密后在存到playload中。
JWT如何实现注销
a.忘记密码、注销等情况可浏览器cookie清除(但是服务器还是存在)
b.建议将时间设置稍微短一点
c.使用黑名单过滤,后期对服务器端压力大
SpringSecurity整合JWT关键性代码
优势: 直接使用令牌信息,减少数据库的查询操作
/**
* Spring Security 整合JWT配置信息
* @param http
* @throws Exception
*/
protected void configure(HttpSecurity http) throws Exception {
List<PermissionEntity> allPermission = permissionMapper.findAllPermission();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
expressionInterceptUrlRegistry = http.authorizeRequests();
allPermission.forEach((permission) -> {
expressionInterceptUrlRegistry.antMatchers(permission.getUrl()).
hasAnyAuthority(permission.getPermTag());
});
expressionInterceptUrlRegistry.antMatchers("/auth/login").permitAll()
.antMatchers("/**").fullyAuthenticated()
.and()
// 设置过滤器
.addFilter(new JWTValidationFilter(authenticationManager()))
.addFilter(new JWTLoginFilter(authenticationManager()))
.csrf().disable()
//剔除session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/**
* Filename: JWTLoginFilter.java
*
* @description: 登录过滤器
*
*/
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
/**
* 获取授权管理
*/
private AuthenticationManager authenticationManager;
public JWTLoginFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
/**
* 后端登陆放行接口设置
*/
super.setFilterProcessesUrl("/auth/login");
}
/**
* 功能描述:真正调用AuthenticationManager的账号密码验证,也就是UserDetailsService方法的判断
*
* @param request
* @param response
*
* @return org.springframework.security.core.Authentication
* <br>-----------------------------------------------------<br>
*
* @author: raby
* @date: 2020/6/8 22:25
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
// 调用我们MemberUserDetailsService 账号密码登陆
try {
UserEntity user = new ObjectMapper()
.readValue(request.getInputStream(), UserEntity.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
logger.error(e.getMessage());
return null;
}
}
/**
* 功能描述:登录成功,则设置令牌信息,从结果中获取数据信息
*
* @param request
* @param response
* @param chain
* @param authResult
*
* @return void
* <br>-----------------------------------------------------<br>
*
* @author: raby
* @date: 2020/6/8 22:25
*/
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException, ServletException {
// 账号和密码验证成功,在响应头中响应的jwt
UserEntity userEntity = (UserEntity) authResult.getPrincipal();
String jwtToken = JaryeJwtUtils.generateJsonWebToken(userEntity);
response.addHeader("token", jwtToken);
}
/**
* 账号密码验证失败
*
* @param request
* @param response
* @param failed
* @throws IOException
* @throws ServletException
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.getWriter().print("账号或者密码错误");
}
}
/**
* Filename: JWTValidationFilter.java
*
* @description: 请求拦截器,判断头部是否带有token等
*
*/
public class JWTValidationFilter extends BasicAuthenticationFilter {
public JWTValidationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
/**
* 拦截请求
*
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication
(setAuthentication(request.getHeader("token")));
super.doFilterInternal(request, response, chain);;
}
/**
* 功能描述:从头部获取token,然后判断是否存在令牌信息,如果有的话则返回令牌及roleList
*
* @param token
*
* @return org.springframework.security.authentication.UsernamePasswordAuthenticationToken
* <br>-----------------------------------------------------<br>
*
* @author: raby
* @date: 2020/6/8 22:28
*/
private UsernamePasswordAuthenticationToken setAuthentication(String token) {
String username = JaryeJwtUtils.getUsername(token);
if (!StringUtils.isEmpty(username)) {
// 解析权限列表
List<SimpleGrantedAuthority> userRoleList = JaryeJwtUtils.getUserRole(token);
return new UsernamePasswordAuthenticationToken(username, null, userRoleList);
}
return null;
}
}
/**
* 功能描述:生成jwt令牌
*
* @param user
*
* @return java.lang.String
* <br>-----------------------------------------------------<br>
*
* @author: raby
* @date: 2020/6/8 22:20
*/
public static String generateJsonWebToken(UserEntity user) {
String token = Jwts
.builder()
.setSubject(SUBJECT)
.claim(ROLE_CLAIMS, user.getAuthorities())
.claim("username", user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRITION))
.signWith(SignatureAlgorithm.HS256, APPSECRET_KEY).compact();
return token;
}
/**
* 功能描述:根据令牌获取用户名信息
*
* @param token
*
* @return java.lang.String
* <br>-----------------------------------------------------<br>
*
* @author: raby
* @date: 2020/6/8 22:22
*/
public static String getUsername(String token) {
Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
return claims.get("username").toString();
}
/**
* 功能描述:根据令牌获取权限列表
*
* @param token
*
* @return java.util.List<org.springframework.security.core.authority.SimpleGrantedAuthority>
* <br>-----------------------------------------------------<br>
*
* @author: raby
* @date: 2020/6/8 22:22
*/
public static List<SimpleGrantedAuthority> getUserRole(String token) {
Claims claims = Jwts.parser().setSigningKey(APPSECRET_KEY).parseClaimsJws(token).getBody();
List roles = (List) claims.get(ROLE_CLAIMS);
String json = JSONArray.toJSONString(roles);
List<SimpleGrantedAuthority>
grantedAuthorityList =
JSONArray.parseArray(json, SimpleGrantedAuthority.class);
return grantedAuthorityList;
}
相关文章链接:
<<<Web常用攻击手段-XSS
<<<Web常用攻击手段-SQL注入
<<<Web常用攻击手段-Http请求防盗链
<<<Web常用攻击手段-CSRF攻击
<<<Web常用攻击手段-上传文件漏洞
<<<Web常用攻击手段-忘记密码
<<<Web常用攻击手段-其他漏洞
<<<安全技术--数据加密/认证技术
<<<安全技术--Https相关知识
<<<安全技术--接口幂等性设计
<<<安全框架--SpringSecurity
<<<安全框架--OAuth2
<<<安全架构整体设计方案
网友评论