Java,SpringBoot采用token方式实现登录认证
IT小奋斗2021-01-13 21:48:33
Token,令牌,访问资源的凭证,每次访问带上这个令牌,就可识别出用户身份。
JWT (JsonWebToken),是实现token技术的一种解决方案,由三部分组成: header(头)、payload(载体)、signature(签名)。
1、头:HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384
2、载体:
iss:Issuer,发行者sub:Subject,主题aud:Audience,观众exp:Expiration time,过期时间nbf:Not beforeiat:Issued at,发行时间jti:JWT ID3、签名
代码案例:
import io.jsonwebtoken.Claims;import io.jsonwebtoken.CompressionCodecs;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.spec.SecretKeySpec;import java.security.Key;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.UUID;public class JJWTTokenApply {// 使用的Keypublic static Key KEY = new SecretKeySpec('密钥'.getBytes(), SignatureAlgorithm.HS512.getJcaName());/** * @param expiration==>失效时间 * @return */public static String generateAccessToken(String subject, Map<String, Object> claimsMap, long expiration) {// 生成Tokenreturn Jwts.builder() .setClaims(claimsMap) .setSubject(subject) .setId(UUID.randomUUID().toString()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() expiration * 1000)) .compressWith(CompressionCodecs.DEFLATE) .signWith(SignatureAlgorithm.HS256, KEY) .compact(); }/** * @param token * @return */public static Map<String, Object> parseAccessToken(String token) {Map<String, Object> claimsMap = new HashMap<String, Object>(16);try { Claims claims = Jwts.parser() .setSigningKey(KEY) .parseClaimsJws(token) .getBody();// 失效时间claimsMap.put('expiration', claims.getExpiration());// 签发者claimsMap.put('created', claims.getIssuedAt()); claimsMap.put('subject', claims.getSubject()); claimsMap.put('user_id', claims.get('user_id')); claimsMap.put('user_info', claims.get('user_info')); claimsMap.put('user_roles', claims.get('user_roles')); } catch (Exception e) { e.printStackTrace(); }return claimsMap; } public static void main(String[] args) {// 主题String subject = 'zhangsan';// 加密数据Map<String, Object> claimsMap = new HashMap<String, Object>(16); claimsMap.put('user_id', 123); claimsMap.put('user_info', '用户信息'); claimsMap.put('user_roles', '用户角色');// 失效期1天String token = generateAccessToken(subject, claimsMap, 86400); System.out.println('accessToken=' token);// 解析tokenMap<String, Object> parseMap = parseAccessToken(token); System.out.println('主题=' parseMap.get('subject')); System.out.println('user_id=' parseMap.get('user_id')); System.out.println('user_info=' parseMap.get('user_info')); System.out.println('user_roles=' parseMap.get('user_roles'));Date expirationDate = (Date) parseMap.get('expiration'); System.out.println('是否失效=' expirationDate.before(new Date())); System.out.println('失效日期=' expirationDate); System.out.println('失效时间=' (expirationDate.getTime() - System.currentTimeMillis())); }}
SpringBoot采用token方式实现认证
1、SpringSecurity的配置
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { CustomAuthenticationProvider customAuthenticationProvider = new CustomAuthenticationProvider(userDetailsService); authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider); }@Overrideprotected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable() .authorizeRequests() // 请求授权// 配置路径放行.antMatchers('/**').permitAll() .antMatchers(HttpMethod.GET, '/webSocket/**').permitAll()// swagger 文档.antMatchers('/swagger-ui.html').permitAll() .antMatchers('/swagger-resources/**').permitAll()// druid.antMatchers('/druid/**').permitAll()// 放行OPTIONS请求.antMatchers(HttpMethod.OPTIONS, '/**').permitAll()// 其他都需要验证.anyRequest().authenticated() .and() .addFilter(new JWTAuthenticationFilter(authenticationManager(), userDetailsService)) .addFilter(new JWTAuthorizationFilter(authenticationManager()))// 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }@BeanCorsConfigurationSource corsConfigurationSource() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration('/**', new CorsConfiguration().applyPermitDefaultValues());return source; }}解析:部分代码没贴,实现的类重点包含:JWTAuthenticationFilter、JWTAuthorizationFilter、CustomAuthenticationProvider
2、登录认证的执行流程

3、代码实现部分
public class CustomAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userService;public CustomAuthenticationProvider(UserDetailsService userService){this.userService = userService; }@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = authentication.getCredentials().toString();// 验证用户和密码返回}@Overridepublic boolean supports(Class<?> authentication) {return authentication.equals(UsernamePasswordAuthenticationToken.class); }}
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;private UserDetailsService userDetailsService;private JwtUtils jwtUtils;public JWTAuthenticationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {this.authenticationManager = authenticationManager;this.userDetailsService = userDetailsService;// 精确指定认证地址super.setFilterProcessesUrl('/api/authen/login'); }@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { Authentication returnAuthentication = null;try { AuthenUser loginUser = new ObjectMapper().readValue(request.getInputStream(), AuthenUser.class); Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>()); returnAuthentication = authenticationManager.authenticate(authenticationToken);return returnAuthentication; } catch (IOException e) { e.printStackTrace(); }return null; }// 成功验证后调用该方法@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authen) throws IOException, ServletException { String username = (String) authen.getPrincipal(); UserDetails userDetail = (UserDetails) userDetailsService.loadUserByUsername(username); String token = jwtUtils.generateAccessToken(userDetail); HashMap<String, Object> resultData = Maps.newHashMap(); resultData.put('token', token); resultData.put('userInfo', userDetail); resultData.put('headerName', JwtUtils.TOKEN_HEADER); resultData.put('prefix', JwtUtils.TOKEN_PREFIX); resultData.put('tokenExpiration', jwtUtils.getExpireTime(jwtUtils.getAccess_token_expiration())); String json = JSONObject.toJSONString(ServerResponse.createBySuccess(resultData)); response.setCharacterEncoding('UTF-8'); response.setContentType('application/json; charset=utf-8'); response.getWriter().write(json); }@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {if (failed instanceof BadCredentialsException) { response.setCharacterEncoding('utf-8'); response.getWriter().write(JSONUtil.toJsonStr(ServerResponse.createDefaultErrorMessage(failed.getMessage()))); } else if (failed instanceof UsernameNotFoundException) { response.setCharacterEncoding('utf-8'); response.getWriter().write(JSONUtil.toJsonStr(ServerResponse.createDefaultErrorMessage(failed.getMessage()))); } else { response.getWriter().write('authentication failed, reason: ' failed.getMessage()); } }}认证登录成功后,返回JSON串,包含token。
3、再次请求

4、代码处理部分
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {super(authenticationManager); }@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String tokenHeader = request.getHeader('Authorization');// HTTP头中的Authorization信息if (tokenHeader == null || !tokenHeader.startsWith('Bearer ')) { chain.doFilter(request, response);return; }try { // 解析token,并且设置认证信息SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader)); } catch (TokenIsExpiredException e) { response.setCharacterEncoding('UTF-8'); response.setContentType('application/json; charset=utf-8'); response.setStatus(HttpServletResponse.SC_FORBIDDEN); String reason = '错误:' e.getMessage(); response.getWriter().write(new ObjectMapper().writeValueAsString(reason)); response.getWriter().flush();return; }super.doFilterInternal(request, response, chain);
赞 (0)
