做外贸的网站哪个好,查流量网站,单位 内网网站建设,中间商可以做网站吗项目集成Spring Security#xff08;一#xff09; 在上一篇基础上继续集成 JWT #xff0c;实现用户身份验证。 前言
前后端分离项目中#xff0c;如果直接把 API 接口对外开放#xff0c;我们知道这样风险是很大的#xff0c;所以在上一篇中我们引入了 Spring Securit…项目集成Spring Security一 在上一篇基础上继续集成 JWT 实现用户身份验证。 前言
前后端分离项目中如果直接把 API 接口对外开放我们知道这样风险是很大的所以在上一篇中我们引入了 Spring Security 但是我们在登陆后缺少了请求凭证部分。
什么是JWT?
JWT是 Json Web Token 的缩写。它是基于 RFC 7519 标准定义的一种可以安全传输的 小巧 和 自包含 的JSON对象。由于数据是使用数字签名的所以是可信任的和安全的。JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名。
JWT的工作流程
1、用户进入登录页输入用户名、密码进行登录 2、服务器验证登录鉴权如果改用户合法根据用户的信息和服务器的规则生成 JWT Token 3、服务器将该 token 以 json 形式返回不一定要json形式这里说的是一种常见的做法 4、用户得到 token存在 localStorage、cookie 或其它数据存储形式中。以后用户请求 /protected 中的 API 时在请求的 header 中加入 Authorization: Bearer xxxx(token)。此处注意token之前有一个7字符长度的 Bearer。 5、服务器端对此 token 进行检验如果合法就解析其中内容根据其拥有的权限和自己的业务逻辑给出对应的响应结果。 6、用户取得结果
如下如所示 来看一下 JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5ctoken 分成了三部分头部(header)荷载(Payload) 和 签名(Signature)每部分用 . 分隔其中头部和荷载使用了base64编码分别解码之后得到两个JSON串
第一部分-头部
{alg: HS256,typ: JWT
}alg字段为加密算法这是告诉我们 HMAC 采用 HS512 算法对 JWT 进行的签名。
第二部分-荷载
{sub: 1234567890,name: John Doe,iat: 1516239022
}荷载的字段及含义
iss: 该JWT的签发者sub: 该JWT所面向的用户aud: 接收该JWT的一方exp(expires): 什么时候过期这里是一个Unix时间戳iat(issued at): 在什么时候签发的
这段告诉我们这个Token中含有的数据声明Claim这个例子里面有三个声明sub, name 和 iat。在我们这个例子中分别代表着 所面向的用户、用户名、创建时间当然你可以把任意数据声明在这里。
第三部分-签名
第三部分签名则不能使用base64解码出来该部分用于验证头部和荷载数据的完整性。
JWT的生成和解析
引入依赖
!-- JWT --
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version
/dependency创建一个测试类尝试一下 JWT 的生成
public class Test {public static void main(String[] args){String token Jwts.builder()主题 放入用户名.setSubject(niceyoo)自定义属性 放入用户拥有请求权限.claim(authorities,admin)失效时间.setExpiration(new Date(System.currentTimeMillis() 7 * 60 * 1000))签名算法和密钥.signWith(SignatureAlgorithm.HS512, tmax).compact();System.out.println(token);}}控制台打印如下
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1ODM1M30.keCiHrcEr0IWXfZLocgHS8znn7uSiaZW1IT6bTs-EQG0NPsb6-Aw_XbGQea4mez2CcAflgMqtzIpsDjZsUOVug数据声明Claim是一个自定义属性可以用来放入用户拥有请求权限。上边为简单直接传了一个 ‘admin’。
再看看解析
public static void main(String[] args){try {解析tokenClaims claims Jwts.parser().setSigningKey(tmax).parseClaimsJws(eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1OTc2Mn0.MkSJtGaVePLa-eM3gylh1T3fwODg-6ceDDOxscXAQKun-qNrbQFcKPNqXhblbXPNLhaJyEnwugNANCTs98UNmA).getBody();System.out.println(claims);获取用户名String username claims.getSubject();System.out.println(username:username);获取权限String authority claims.get(authorities).toString();System.out.println(权限authority);} catch (ExpiredJwtException e) {System.out.println(jwt异常);} catch (Exception e){System.out.println(异常);}
}控制台打印
{subniceyoo, authoritiesadmin, exp1559459762}
username:niceyoo
权限adminJWT 本身没啥难度但安全整体是一个比较复杂的事情JWT 只不过提供了一种基于 token 的请求验证机制。但我们的用户权限对于 API 的权限划分、资源的权限划分用户的验证等等都不是JWT负责的。也就是说请求验证后你是否有权限看对应的内容是由你的用户角色决定的。所接下来才是我们的重点Spring Security 整合 JWT。
集成JWT
要想要 JW T在 Spring 中工作我们应该新建一个 JWT filter并把它配置在 WebSecurityConfig 中。
WebSecurityConfigurerAdapter.java
Slf4j
Configuration
EnableGlobalMethodSecurity(prePostEnabledtrue)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate UserDetailsServiceImpl userDetailsService;Autowiredprivate AuthenticationSuccessHandler successHandler;Autowiredprivate AuthenticationFailHandler failHandler;Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());加密}Overrideprotected void configure(HttpSecurity http) throws Exception {ExpressionUrlAuthorizationConfigurerHttpSecurity.ExpressionInterceptUrlRegistry registry http.authorizeRequests();registry.and()表单登录方式.formLogin().permitAll()成功处理类.successHandler(successHandler)失败.failureHandler(failHandler).and().logout().permitAll().and().authorizeRequests()任何请求.anyRequest()需要身份认证.authenticated().and()关闭跨站请求防护.csrf().disable()前后端分离采用JWT 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()添加JWT过滤器 除已配置的其它请求都需经过此过滤器.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));}
}相较于上一篇主要多了如下一行配置
.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));JWTAuthenticationFilter.java
Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {private Integer tokenExpireTime;public JWTAuthenticationFilter(AuthenticationManager authenticationManager, Integer tokenExpireTime) {super(authenticationManager);this.tokenExpireTime tokenExpireTime;}public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {super(authenticationManager, authenticationEntryPoint);}Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String header request.getHeader(SecurityConstant.HEADER);if(StrUtil.isBlank(header)){header request.getParameter(SecurityConstant.HEADER);}Boolean notValid StrUtil.isBlank(header) || (!header.startsWith(SecurityConstant.TOKEN_SPLIT));if (notValid) {chain.doFilter(request, response);return;}try {UsernamePasswordAuthenticationToken 继承 AbstractAuthenticationToken 实现 Authentication所以当在页面中输入用户名和密码之后首先会进入到 UsernamePasswordAuthenticationToken验证(Authentication)UsernamePasswordAuthenticationToken authentication getAuthentication(header, response);SecurityContextHolder.getContext().setAuthentication(authentication);}catch (Exception e){e.toString();}chain.doFilter(request, response);}private UsernamePasswordAuthenticationToken getAuthentication(String header, HttpServletResponse response) {用户名String username null;权限ListGrantedAuthority authorities new ArrayList();try {解析tokenClaims claims Jwts.parser().setSigningKey(SecurityConstant.JWT_SIGN_KEY).parseClaimsJws(header.replace(SecurityConstant.TOKEN_SPLIT, )).getBody();logger.info(claimsclaims);获取用户名username claims.getSubject();logger.info(usernameusername);获取权限String authority claims.get(SecurityConstant.AUTHORITIES).toString();logger.info(authorityauthority);if(!StringUtils.isEmpty(authority)){authorities.add(new SimpleGrantedAuthority(authority));}} catch (ExpiredJwtException e) {ResponseUtil.out(response, ResponseUtil.resultMap(false,401,登录已失效请重新登录));} catch (Exception e){log.error(e.toString());ResponseUtil.out(response, ResponseUtil.resultMap(false,500,解析token错误));}if(StrUtil.isNotBlank(username)) {踩坑提醒 此处password不能为nullUser principal new User(username, , authorities);return new UsernamePasswordAuthenticationToken(principal, null, authorities);}return null;}
}接下来我们启动项目看看
访问项目中已有的链接
http://localhost:7777/tmax/videoCategory/getAll老样子认证一波 其中 niceyoo、****** 为数据库用户信息
登陆成功后获取返回的 token注意此 token 是由 JWT 生成的 String token SecurityConstant.TOKEN_SPLIT Jwts.builder()主题 放入用户名.setSubject(username)自定义属性 放入用户拥有请求权限.claim(SecurityConstant.AUTHORITIES, authorities)失效时间.setExpiration(new Date(System.currentTimeMillis() 7 * 60 * 1000))签名算法和密钥.signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY).compact();浏览器返回 token 如下 然后我们通过 token 凭证去访问上边的方法 后台打印信息
claims{subniceyoo, authoritiesadmin, exp1559472866}
usernameniceyoo
authorityadmin随便改一下 token 返回如下