Spring Security JWT
首先需要创建2个过滤器. JwtLoginFilter 和 JwtAuthenticationFilter .
JwtLoginFilter 用来处理用户登录请求.
JwtAuthenticationFilter 用来处理JwtToken的验证解析.
/** * @author: 阮胜 * @date: 2018/7/10 8:42 */ public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter { private static final String POST = "POST"; private AuthenticationSuccessHandler successHandler = new JwtLoginSucessHandler(); private AuthenticationFailureHandler failureHandler = new JwtLoginFailureHandler(); public JwtLoginFilter(AuthenticationManager authenticationManager) { super(new AntPathRequestMatcher("/user/login", "POST")); setAuthenticationManager(authenticationManager); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { if (!request.getMethod().equals(POST)) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String username = request.getParameter("username"); String password = request.getParameter("password"); if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { PrintWriter writer = response.getWriter(); writer.write("用户名或者密码为空"); writer.close(); return null; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); return this.getAuthenticationManager().authenticate(authRequest); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { JwtTokenUtil jwtTokenUtil = new JwtTokenUtil(); JwtToken jwtToken = new JwtToken(authResult.getName(), authResult.getAuthorities().iterator().next().toString(), jwtTokenUtil.generateExpirationDate()); String jwtTokenStr = jwtTokenUtil.generateToken(jwtToken); response.addHeader("Authorization", jwtTokenStr); successHandler.onAuthenticationSuccess(request, response, authResult); } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { failureHandler.onAuthenticationFailure(request, response, failed); } public void setSuccessHandler(AuthenticationSuccessHandler successHandler) { this.successHandler = successHandler; } public void setFailureHandler(AuthenticationFailureHandler failureHandler) { this.failureHandler = failureHandler; } }
/** * @author: 阮胜 * @date: 2018/7/10 10:44 */ public class JwtAuthenticationFilter extends BasicAuthenticationFilter { public JwtAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String jwtTokenStr = request.getHeader("Authorization"); if (!StringUtils.isEmpty(jwtTokenStr)) { try { if (!jwtTokenUtil.validateToken(jwtTokenStr)) { throw new InvalidJwtTokenException(); } JwtToken jwtToken = jwtTokenUtil.parseJwtToken(jwtTokenStr); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(jwtToken.getUsername(), null , AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_".concat(jwtToken.getRole()))); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } catch (Exception e) { sendError(response, "无效的Token"); return; } } chain.doFilter(request, response); } private void sendError(HttpServletResponse response, String msg) throws IOException { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setContentType("text/plain;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(msg); writer.close(); } }
配置类:
/** * @author: 阮胜 * @date: 2018/7/10 8:33 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { private final AccessDeniedHandler accessDeniedHandler; private final AuthenticationEntryPoint authenticationEntryPoint; private final UserDetailServiceImpl userDetailsService; public SecurityConfig(AccessDeniedHandler accessDeniedHandler, AuthenticationEntryPoint authenticationEntryPoint, UserDetailServiceImpl userDetailsService) { this.accessDeniedHandler = accessDeniedHandler; this.authenticationEntryPoint = authenticationEntryPoint; this.userDetailsService = userDetailsService; } @Bean public PasswordEncoder passwordEncoder() { return new DefaultEncoder(); } @Bean public JwtLoginFilter jwtLoginFilter() throws Exception { JwtLoginFilter jwtLoginFilter = new JwtLoginFilter(authenticationManager()); jwtLoginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> { System.out.println("success"); }); jwtLoginFilter.setAuthenticationFailureHandler((request, response, exception) -> { System.out.println("false"); }); return jwtLoginFilter; } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { return new JwtAuthenticationFilter(authenticationManager()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/admin", "/admin/info").hasRole("ADMIN") .anyRequest().permitAll() .and().userDetailsService(userDetailsService) //如果已经登录,但没有访问资源的权限,则调用该Handler .exceptionHandling().accessDeniedHandler(accessDeniedHandler) //如果未登录,没有权限则调用该EntryPoint .authenticationEntryPoint(authenticationEntryPoint) // 无状态的Session机制(即Spring不使用HTTPSession),对于所有的请求都做权限校验 .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //关闭跨域保护 .and().csrf().disable(); //把自己写的2个filter加入到过滤器链中 http.addFilterBefore(jwtLoginFilter(), UsernamePasswordAuthenticationFilter.class) .addFilter(jwtAuthenticationFilter()); } }
工具类:
package com.example.springsecurityjwtdemo.util; import com.example.springsecurityjwtdemo.exception.InvalidJwtTokenException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * @author 阮胜 * @date 2018/7/5 21:21 */ @Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -3301605591108950415L; public static final String USERNAME = "username"; public static final String ROLE = "role"; public static final String CREATED_DATE = "createdDate"; private static final String SECRET = "jwt_secret"; private static final int EXPIRED_TIME_SECONDS = 60 * 60 * 24 * 7; public JwtToken parseJwtToken(String token) throws InvalidJwtTokenException { Claims claims = obtainClaims(token); if (claims == null) { throw new InvalidJwtTokenException(); } return new JwtToken(claims.get(USERNAME).toString(), claims.get(ROLE).toString(), claims.get(CREATED_DATE, Date.class)); } public String obtainUsername(String token) { String username; try { final Claims claims = obtainClaims(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } public Date obtainExpiredDate(String token) { Date expiration; try { final Claims claims = obtainClaims(token); expiration = claims.getExpiration(); } catch (Exception e) { expiration = null; } return expiration; } private Claims obtainClaims(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; } public Date generateExpirationDate() { long expired = System.currentTimeMillis() + EXPIRED_TIME_SECONDS * 1000; return new Date(expired); } private boolean isTokenExpired(String token) { final Date expiration = obtainExpiredDate(token); return expiration.after(new Date()); } public String generateToken(UserDetails userDetails) { return generateToken( new JwtToken(userDetails.getUsername() , userDetails.getAuthorities().iterator().next().toString() , new Date())); } private String generateToken(Map<String, Object> claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpirationDate()) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } public String generateToken(JwtToken jwtToken) { HashMap<String, Object> tokenMap = new HashMap<>(3); tokenMap.put(JwtTokenUtil.USERNAME, jwtToken.getUsername()); tokenMap.put(JwtTokenUtil.CREATED_DATE, jwtToken.getExpiredDate()); tokenMap.put(JwtTokenUtil.ROLE, jwtToken.getRole()); return generateToken(tokenMap); } public boolean validateToken(String token) { Date expiredDate = obtainExpiredDate(token); return expiredDate != null && expiredDate.after(new Date()); } }
package com.example.springsecurityjwtdemo.util; import lombok.Data; import java.util.Date; /** * @author 阮胜 * @date 2018/7/5 20:54 */ @Data public class JwtToken { private String username; private String role; private Date expiredDate; public JwtToken() { } public JwtToken(String username, String role, Date expiredDate) { this.username = username; this.role = role; this.expiredDate = expiredDate; } }