redis jwt spring boot spring security 實現api token 驗證
文章地址:http://www.haha174.top/article/details/258083
項目源碼:https://github.com/haha174/jwt-token.git
具體的實際效果可以看考這裏 目前已經部署一個 個人測試機器上面:
http://cloud.codeguoj.cn/api-cloud-server/swagger-ui.html#!/token45controller/loginUsingPOST
相信很多人都調用過api, 一般的大致基本步驟都是先用登陸獲得一個token,然後使用token調用api 或者直接給你一個token 憑借token調用api.
那麽這裏實現一個spring security+ redis+ jwt 發token 和驗證token的一個項目。
首先需要安裝redis 如果不會的小夥伴可以參考http://www.haha174.top/article/details/257478 這篇博客。
創建一個maven項目導入依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wen.security</groupId> <artifactId>jwt-token</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-parent</artifactId> <version>1.5.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.6</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.6.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.44</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> </dependencies> </project>
編寫 token 攔截器 通過重寫springsecurity AbstractAuthenticationProcessingFilter
public class AuthTokenFilter extends AbstractAuthenticationProcessingFilter { Logger logger = LoggerFactory.getLogger(this.getClass()); @Value("${token.header}") private String tokenHeader; @Value("${token.name}") private String tokenName; public AuthTokenFilter(RequestMatcher matcher) { super(matcher); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String authToken = request.getHeader(this.tokenHeader); if(StringUtils.isEmpty(authToken)) { authToken = request.getParameter(this.tokenName); } logger.debug("start to check token:{} *************"); if (authToken == null) { throw new AuthenticationCredentialsNotFoundException("Access Token is not provided"); } UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authToken, null); return getAuthenticationManager().authenticate(authentication); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException, ServletException { SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(auth); SecurityContextHolder.setContext(context); chain.doFilter(request, response); } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { SecurityContextHolder.clearContext(); response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); String message; if (authException.getCause() != null) { message = authException.getCause().getMessage(); } else { message = authException.getMessage(); } BaseResponse baseResponse = new BaseResponse(); baseResponse.setStatusCode(IConstants.RESPONSE_STATUS_CODE_FAILED); baseResponse.setStatusMsg(message); byte[] body = new ObjectMapper().writeValueAsBytes(baseResponse); response.getOutputStream().write(body); } }
自定義校驗
@Component
public class AuthTokenProvider implements AuthenticationProvider
{
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
protected HttpServletRequest request;
@Autowired
TokenService tokenService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth!=null && auth.isAuthenticated())
{
return new UsernamePasswordAuthenticationToken(auth.getPrincipal(), null, new ArrayList<>());
}
String token = (String) authentication.getPrincipal();
if (token != null)
{
if (!tokenService.checkToken(token))
{
throw new CredentialsExpiredException("Access Token is expired. Please login again.");
}
}
else
{
throw new BadCredentialsException("Invalid token String.");
}
logger.debug("Authenticated successfully.");
return new UsernamePasswordAuthenticationToken(token, null, new ArrayList<>());
}
@Override
public boolean supports(Class<?> authentication)
{
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
配置spring security 將上述的攔截器配置到spring security 的攔截器鏈上 和攔截規則
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebAuthConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public AuthTokenFilter authenticationTokenFilterBean() throws Exception {
List<String> pathsToSkip = Arrays.asList("/login");
List<String> processingPath = Arrays.asList("/service/**","/logout");
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, processingPath);
AuthTokenFilter filter = new AuthTokenFilter(matcher);
filter.setAuthenticationManager(authenticationManager);
return filter;
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/", "/*.html", "/**/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
.and().addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.cors().configurationSource(corsConfigurationSource());
httpSecurity.logout().disable();
httpSecurity.headers().cacheControl();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return filter;
}
}
生成token 使用json web toekn
public class JavaWebToken {
private static Logger log = LoggerFactory.getLogger(JavaWebToken.class);
//該方法使用HS256算法和Secret:bankgl生成signKey
private static Key getKeyInstance() {
//We will sign our JavaWebToken with our ApiKey secret
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("token");
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
return signingKey;
}
//使用HS256簽名算法和生成的signingKey最終的Token,claims中是有效載荷
public static String createJavaWebToken(Map<String, Object> claims) {
return Jwts.builder()
.setIssuer("Jersey-Security-Basic")//設置發行人
.setSubject("subject")//設置抽象主題
.setAudience("login")//設置角色
.setExpiration(getDate())//過期時間
.setIssuedAt(new Date())//設置現在時間
.setClaims(claims)
.signWith( SignatureAlgorithm.HS256,getKeyInstance())
.compact();
}
//解析Token,同時也能驗證Token,當驗證失敗返回null
public static Map<String, Object> parserJavaWebToken(String jwt) {
try {
Map<String, Object> jwtClaims =
Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwt).getBody();
return jwtClaims;
} catch (Exception e) {
log.error("json web token verify failed");
return null;
}
}
public static Date getDate(){
try {
/* SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date=format.parse(new Date().getTime()+new TokenProperties().getExpires()*1000+"");
return date;*/
long currentTime = System.currentTimeMillis() ;
currentTime +=30*60*1000*2;
Date date=new Date(currentTime);
return date;
}catch (Exception e){
e.printStackTrace();
long currentTime = System.currentTimeMillis() ;
currentTime +=30*60*1000*2;
Date date=new Date(currentTime);
return date;
}
}
}
配置swagger
在請求頭中加入header
@Bean
public Docket demoApi() {
ParameterBuilder aParameterBuilder = new ParameterBuilder();
aParameterBuilder.name("Authorization").description("input the token for authentication either in the authorization field or in the token field").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
ParameterBuilder aParameterBuilder1 = new ParameterBuilder();
aParameterBuilder1.name("token").description("input the token for authentication either in the authorization field or in the token field").modelRef(new ModelRef("string")).parameterType("query").required(false).build();
List<Parameter> aParameters = new ArrayList<Parameter>();
aParameters.add(aParameterBuilder.build());
aParameters.add(aParameterBuilder1.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(getApiInfo()).useDefaultResponseMessages(false).globalOperationParameters(aParameters)
.select() .apis(RequestHandlerSelectors.basePackage("com.wen.token.web")).build();
}
protected ApiInfo getApiInfo()
{
return new ApiInfo("Rest Web Service", "total api Rest Web Service " + new Date(), "", "",
new Contact("cxhc", "", ""), "", "",new ArrayList<VendorExtension>());
}
整合redis 可以參考這篇博客
http://www.haha174.top/article/details/251216
只需要啟動應用 訪問http://localhost:8080/swagger-ui.html#/
----
redis jwt spring boot spring security 實現api token 驗證