-
jwt
-
简介
JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
-
特点
简洁(Compact)
可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。自包含(Self-contained)
负载中包含了所有用户所需要的信息,避免了多次查询数据库。
-
java平台的使用
-
maven 支持
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency>
-
生成 token
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.crypto.MacProvider; import java.security.Key; // We need a signing key, so we'll create one just for this example. Usually // the key would be read from your application configuration instead. Key key = MacProvider.generateKey(); String compactJws = Jwts.builder() .setSubject("Joe") .signWith(SignatureAlgorithm.HS512, key) .compact();
-
解析token
String compactJws = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.yiV1GWDrQyCeoOswYTf_xvlgsnaVVYJM0mU6rkmRBf2T1MBl3Xh2kZii0Q9BdX5-G0j25Qv2WF4lA6jPl5GKuA"; try { Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws); //OK, we can trust this JWT } catch (SignatureException e) { //don't trust the JWT! }
-
-
-
spring-security + jwt 实现思路
-
在spring-security原本的FilterChain中,
添加 jwt认证用的Filter :JwtAuthenticationTokenFilter。 -
客户端请求认证流程
-
具体实现
-
application.properties 配置
##============JSON Web Token======================================== jwt.header=Authorization jwt.secret=mySecret jwt.expiration=604800 jwt.route.authentication.path=auth jwt.route.authentication.refresh=refresh jwt.route.authentication.register="auth/register"
添加 JwtAuthenticationTokenFilter
@Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { private final Log logger = LogFactory.getLog(this.getClass()); @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Value("${jwt.header}") private String tokenHeader; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { System.out.println("进来了 JwtAuthenticationTokenFilter"); // 得到 请求头的 认证信息 authToken String authToken = request.getHeader(this.tokenHeader); // 解析 authToken 得到 用户名 String username = jwtTokenUtil.getUsernameFromToken(authToken); System.out.println("checking authentication for user " + username); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { // 根据用户名从数据库查找用户信息 UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); // 检验token是否有效,并检验其保存的用户信息是否正确 if (jwtTokenUtil.validateToken(authToken, userDetails)) { // token 有效,为该请求装载 用户权限信息 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); logger.info("authenticated user " + username + ", setting security context"); SecurityContextHolder.getContext().setAuthentication(authentication); } } System.out.println("出去了 JwtAuthenticationTokenFilter"); chain.doFilter(request, response); } }
- 将 JwtAuthenticationTokenFilter 添加到 FilterChain 中
@EnableWebSecurity public class MultiHttpSecurityConfig { @Configuration public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; // 静态资源访问的 url private String[] staticFileUrl = {}; // 不用认证就可访问的 url private String[] permitUrl = {}; @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers(staticFileUrl); web.ignoring().antMatchers(permitUrl); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); // 访问url认证 http .authorizeRequests() .antMatchers("/admin/**").hasAuthority(String.valueOf(AuthorityName.ROLE_ADMIN)) .anyRequest().authenticated(); // 配置登陆信息 http .formLogin().loginPage("/login") .defaultSuccessUrl("/goIndex") .permitAll() .and(); // 配置退出登陆信息 http .logout() .logoutSuccessUrl("/login") .invalidateHttpSession(true) .deleteCookies() .and(); http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class); http.httpBasic(); } } }
- 提供一个jwt认证服务:提供 jwt token 的 生成和更新 功能
(其实就是一个controller)
@RestController @RequestMapping("authentication") public class AuthenticationRestController { private final Log logger = LogFactory.getLog(this.getClass()); @Value("${jwt.header}") private String tokenHeader; @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private UserDetailsService userDetailsService; @RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST) public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest, Device device) throws AuthenticationException { System.out.println("进来了 createAuthenticationToken "); System.out.println("authenticationRequest : " + authenticationRequest.getPassword() + "::" + authenticationRequest.getUsername()); // Perform the security final Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( authenticationRequest.getUsername(), authenticationRequest.getPassword() ) ); SecurityContextHolder.getContext().setAuthentication(authentication); // Reload password post-security so we can generate token final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername()); final String token = jwtTokenUtil.generateToken(userDetails, device); // Return the token return ResponseEntity.ok(new JwtAuthenticationResponse(token)); } }
-
至此,spring-security 整合 jwt 认证 就已经完成了。
-
参考文档