java工作流中的token,JWT(JSON Web Tokens)在Spring 微服务中的应用

关于JWT,之前的文章详细介绍,本篇我们将介绍JWT在Spring微服务中的应用。

工作流程

首先,客户端通过用户名和密码发起请求到开放接口,服务端收到请求之后,对用户名和密码进行验证,验证通过之后,生成JWT并响应给客户端。客户端收到服务器响应之后,将JWT保存到本地,后续客户端的所有请求的HTTP Header都将包含此Token信息,进而服务器端可以对所有请求进行授权验证。

JWT的生成与验证

public class JsonWebTokenUtility {

private SignatureAlgorithm signatureAlgorithm;

private Key secretKey;

public JsonWebTokenUtility() {

// THIS IS NOT A SECURE PRACTICE!

// For simplicity, we are storing a static key here.

// Ideally, in a microservices environment, this key would kept on a

// config server.

signatureAlgorithm = SignatureAlgorithm.HS512;

String encodedKey =

"L7A/6zARSkK1j7Vd5SDD9pSSqZlqF7mAhiOgRbgv9Smce6tf4cJnvKOjtKPxNNnWQj+2lQEScm3XIUjhW+YVZg==";

secretKey = deserializeKey(encodedKey);

}

public String createJsonWebToken(AuthTokenDetailsDTO authTokenDetailsDTO) {

String token = Jwts.builder().setSubject(authTokenDetailsDTO.userId).claim

("email", authTokenDetailsDTO.email)

.claim("roles", authTokenDetailsDTO.roleNames).setExpiration

(authTokenDetailsDTO.expirationDate)

.signWith(getSignatureAlgorithm(), getSecretKey()).compact();

return token;

}

private Key deserializeKey(String encodedKey) {

byte[] decodedKey = Base64.getDecoder().decode(encodedKey);

Key key = new SecretKeySpec(decodedKey, getSignatureAlgorithm().getJcaName());

return key;

}

private Key getSecretKey() {

return secretKey;

}

public SignatureAlgorithm getSignatureAlgorithm() {

return signatureAlgorithm;

}

public AuthTokenDetailsDTO parseAndValidate(String token) {

AuthTokenDetailsDTO authTokenDetailsDTO = null;

try {

Claims claims = Jwts.parser().setSigningKey

(getSecretKey()).parseClaimsJws(token).getBody();

String userId = claims.getSubject();

String email = (String) claims.get("email");

List roleNames = (List) claims.get("roles");

Date expirationDate = claims.getExpiration();

authTokenDetailsDTO = new AuthTokenDetailsDTO();

authTokenDetailsDTO.userId = userId;

authTokenDetailsDTO.email = email;

authTokenDetailsDTO.roleNames = roleNames;

authTokenDetailsDTO.expirationDate = expirationDate;

} catch (JwtException ex) {

System.out.println(ex);

}

return authTokenDetailsDTO;

}

private String serializeKey(Key key) {

String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());

return encodedKey;

}

}

微服务中的Spring Security配置

Spring Security中权限处理由AuthenticationManager组件完成,我们需要在AuthenticationManager注册一个自定义拦截器JsonWebTokenAuthenticationFilter拦截请求(JsonWebTokenAuthenticationFilter继承RequestHeaderAuthenticationFilter),JsonWebTokenAuthenticationFilter负责读取请求报头中是否包含Authorization信息:

public class JsonWebTokenAuthenticationFilter extends RequestHeaderAuthenticationFilter {

public JsonWebTokenAuthenticationFilter() {

// Don't throw exceptions if the header is missing

this.setExceptionIfHeaderMissing(false);

// This is the request header it will look for

this.setPrincipalRequestHeader("Authorization");

}

@Override

@Autowired

public void setAuthenticationManager(AuthenticationManager authenticationManager) {

super.setAuthenticationManager(authenticationManager);

}

}

在这一步中,请求报头将以PreAuthenticatedAuthenticationToken的形式转换为Spring Authentication对象,拦截器读取转换完之后,我们需要在Authentication Manager中注册Provider来读取、解析,并将其转换为自定义验签对象,Provider实现如下:

public class JsonWebTokenAuthenticationProvider implements AuthenticationProvider {

private JsonWebTokenUtility tokenService = new JsonWebTokenUtility();

@Override

public Authentication authenticate(Authentication authentication) throws AuthenticationException {

Authentication authenticatedUser = null;

// Only process the PreAuthenticatedAuthenticationToken

if (authentication.getClass().isAssignableFrom(PreAuthenticatedAuthenticationToken.class)

&& authentication.getPrincipal() != null) {

String tokenHeader = (String) authentication.getPrincipal();

UserDetails userDetails = parseToken(tokenHeader);

if (userDetails != null) {

authenticatedUser = new JsonWebTokenAuthentication(userDetails, tokenHeader);

}

} else {

// It is already a JsonWebTokenAuthentication

authenticatedUser = authentication;

}

return authenticatedUser;

}

private UserDetails parseToken(String tokenHeader) {

UserDetails principal = null;

AuthTokenDetailsDTO authTokenDetails = tokenService.parseAndValidate(tokenHeader);

if (authTokenDetails != null) {

List authorities = authTokenDetails.roleNames.stream()

.map(roleName -> new SimpleGrantedAuthority(roleName)).collect(Collectors.toList());

principal = new User(authTokenDetails.email, "", authorities);

}

return principal;

}

@Override

public boolean supports(Class> authentication) {

return authentication.isAssignableFrom(PreAuthenticatedAuthenticationToken.class)

|| authentication.isAssignableFrom(JsonWebTokenAuthentication.class);

}

}

Spring Security Java Configuration

配置Spring Security Java Configuration,注册Authentication Manager Bean

public abstract class JsonWebTokenSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

private JsonWebTokenAuthenticationProvider authenticationProvider;

@Autowired

private JsonWebTokenAuthenticationFilter jsonWebTokenFilter;

@Bean

@Override

public AuthenticationManager authenticationManagerBean() throws Exception {

// This method is here with the @Bean annotation so that Spring can

// autowire it

return super.authenticationManagerBean();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.authenticationProvider(authenticationProvider);

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http

// disable CSRF, http basic, form login

.csrf().disable() //

.httpBasic().disable() //

.formLogin().disable()

// ReST is stateless, no sessions

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //

.and()

// return 403 when not authenticated

.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());

// Let child classes set up authorization paths

setupAuthorization(http);

http.addFilterBefore(jsonWebTokenFilter, UsernamePasswordAuthenticationFilter.class);

}

protected abstract void setupAuthorization(HttpSecurity http) throws Exception;

}

定义与调用

微服务与微服务之间的接口调用,我们可以使用@FeignClient来进行调用,比如,端到端的服务调用定义:

@FeignClient("user-management-service")

public interface UserManagementServiceAPI {

@RequestMapping(method = RequestMethod.POST, value = "/roles")

RoleDTO createRole(@RequestHeader("Authorization")

String authorizationToken, @RequestBody RoleDTO roleDTO);

}

@RequestHeader(“Authorization”) String authorizationToken

服务之间端到端的服务调用可以通过下面这种方式来获取Token

private String getAuthorizationToken() {

String token = null;

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication != null &&

authentication.getClass().isAssignableFrom(JsonWebTokenAuthentication.class)) {

JsonWebTokenAuthentication jwtAuthentication = (JsonWebTokenAuthentication) authentication;

token = jwtAuthentication.getJsonWebToken();

}

return token;

}

客户端请求可以通过设置ajax的headers参数的Authorization请求参数

$.ajax({

url: '',

headers:{Authorization:'token'},

...

})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值