java json web token_使用JSON Web Tokens和Spring实现微服务

本文介绍了如何使用JSON Web Tokens(JWT)与Spring框架实现微服务的授权。JWT可以减少会话Session的开销,允许在分布式系统中安全地传递用户信息。通过创建JWT工具类、自定义授权过滤器和认证提供者,实现了标准的Spring Security机制,并展示了如何在微服务间传递JWT以确保授权。
摘要由CSDN通过智能技术生成

使用JSON Web Tokens和Spring实现微服务

JSON Web Token (JWT) 是一个自我认证的记号,能够包含用户标识、角色和用户权限等信息,能够被任何人方便解析和使用安全的key实现验证,具体可参考这里。

结合JWT的微服务能够避免会话Session开销,同时方便跨多机分布式系统,这样不需要每个服务都要调用专门的授权服务以确认用户操作本服务的权限。另外一个优点是JWT足够小,能够被序列化附加在请求头部内。

第一个带有用户名和密码的请求提交POST到一个未受保护的登录授权REST端点,一旦用户和密码通过授权,响应中将包含一个JWT,以后的请求都会把这个JWT记号带在HTTP头部中,形式像: Authorization: xxxxx.yyyyy.zzzzz

任何服务到服务的请求会一路传递这个请求头部,这样授权信息都可以应用在一路传递的服务中,这些服务都可以检查这个JWT,以决定是否接受访问。

下面以Spring cloud代码为案例,使用JWT的Java实现: Java JWT

public class JsonWebTokenUtility {

private SignatureAlgorithm signatureAlgorithm;

private Key secretKey;

public JsonWebTokenUtility() {

// 这里不是真正安全的实践

// 为了简单,我们存储一个静态key在这里,

// 在真正微服务环境,这个key将会被保留在配置服务器

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中已经有一个这样的授权Filter称为:RequestHeaderAuthenticationFilter,我们只要扩展继承即可:

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);

}

}

在这里,头部信息将被转换为Spring Authentication对象,名称为PreAuthenticatedAuthenticationToken

我们需要一个授权提供者读取这个记号,然偶验证它,然后转换为我们自己的定制授权对象:

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);

}

}

使用这些组件,我们现在就可以有使用JWT的标准Spring Securtiy安全机制,当我们进行服务对服务调用时,我们需要一路传输JWT。

这里虚构一个客户端,将JWT作为参数传递:

@FeignClient("user-management-service")

public interface UserManagementServiceAPI {

@RequestMapping(value = "/authenticate", method = RequestMethod.POST)AuthTokenDTO authenticateUser(@RequestBody AuthenticationDTO authenticationDTO);

@RequestMapping(method = RequestMethod.POST, value = "/roles")RoleDTO createRole(@RequestHeader("Authorization") String authorizationToken, @RequestBody RoleDTO roleDTO);

@RequestMapping(method = RequestMethod.POST, value = "/users")UserDTO createUser(@RequestHeader("Authorization") String authorizationToken, @RequestBody UserDTO userDTO);

@RequestMapping(method = RequestMethod.DELETE, value = "/roles/{id}")void deleteRole(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);

@RequestMapping(method = RequestMethod.DELETE, value = "/users/{id}")void deleteUser(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);

@RequestMapping(method = RequestMethod.GET, value = "/roles")Collection findAllRoles(@RequestHeader("Authorization") String authorizationToken);

@RequestMapping(method = RequestMethod.GET, value = "/users")Collection findAllUsers(@RequestHeader("Authorization") String authorizationToken);

@RequestMapping(method = RequestMethod.GET, value = "/roles/{id}", produces = "application/json", consumes = "application/json")RoleDTO findRoleById(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);

@RequestMapping(method = RequestMethod.GET, value = "/users/{id}", produces = "application/json", consumes = "application/json")UserDTO findUserById(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);

@RequestMapping(method = RequestMethod.GET, value = "/users/{id}/roles")Collection findUserRoles(@RequestHeader("Authorization") String authorizationToken,@PathVariable("id") int id);

@RequestMapping(method = RequestMethod.PUT, value = "/roles/{id}")void updateRole(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id,@RequestBody RoleDTO roleDTO);

@RequestMapping(method = RequestMethod.PUT, value = "/users/{id}")void updateUser(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id,@RequestBody UserDTO userDTO);

}

为了一路传递JWT,在我们自己的控制器中模仿Spring Security实现如下:

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;

}

正如你看到,在一个分布式微服务环境中,JWT提供了灵活的授权。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值