博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[译] JWT 与 Spring Cloud 微服务
阅读量:6979 次
发布时间:2019-06-27

本文共 8546 字,大约阅读时间需要 28 分钟。

作者:THOMAS KENDALL
译者:oopsguy.com

微服务安全是架构的一个重要部分。具体来说,就是认证和授权模式。

微服务认证和授权处理方式有几种选择,但本文只介绍 JSON Web Token 的使用。

JSON Web Token

JSON Web Token(JWT)本质上是一个独立的身份验证令牌,可以包含用户标识、用户角色和权限等信息,以及您可以存储任何其他信息。任何人都可以轻松读取和解析,并使用密钥来验证真实性。有关 JSON Web Token 的简要介绍,请查看。

微服务使用 JSON Web Token 的一个优点是,我们可以配置它以便包含用户拥有的任何权限。这意味着每个服务不需要与授权服务交互才能授权用户。

JWT 的另一个优点是它们是可序列化的,足够短的长度使得它可以放置在请求头中。

工作原理

JWT 的工作流程相当简单。第一次请求是一个带有用户名和密码的无身份验证端点的 POST。

认证成功后,响应将包含 JWT。之后所有的请求都附带一个 HTTP 头,其包含了 JWT 令牌:Authorization: xxxxx.yyyyy.zzzzz

任何服务间的请求都要通过该头,以便其他服务可以应用授权。

开始编码

我们需要做的第一件事是弄清楚如何生成 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 有一个认证过滤器 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);    }}复制代码

此时,头已经以 PreAuthenticatedAuthenticationToken 的形式转换为 Spring Authentication 对象。

我们现在需要一个 AuthenticationProvider 用于读取令牌,从而进行身份验证,并将其转换为我们自己自定义的 Authentication 对象。

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 中使用 JWT 了。 在进行服务间通信时,我们需要传递 JWT。

我使用了一个 Feign 客户端,把 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 可以很好地适应分布式微服务环境,并提供了大量功能。 如果您正想为下一个微服务项目设计一个安全架构,请考虑使用 JSON Web Token。

转载地址:http://uljpl.baihongyu.com/

你可能感兴趣的文章
你一定不知道IDE里的Tomcat是怎么工作的
查看>>
Typescript 2+迷你书 :从入门到不放弃
查看>>
Android studio 跟踪 Gradle Task 执行
查看>>
Java面试通关要点汇总集
查看>>
从源码角度理解Handler、Looper、MessageQueue之间关系
查看>>
Bitmap 比你想的更费内存 | 吊打 OOM
查看>>
为你揭秘小程序音视频背后的故事......
查看>>
自定义侧边快速索引栏
查看>>
一种自动化检测 Flash 中 XSS 方法的探讨
查看>>
基于环信sdk实现简单即时聊天
查看>>
Java基础-Synchronized原理
查看>>
大道至简,阿里巴巴敏捷教练的电子看板诞生记
查看>>
zookeeper学习04 zookeeper收尾+dubbo前瞻
查看>>
《讲个故事》为什么IP地址与Mac地址 缺一不可
查看>>
华山论剑之浅谈iOS的生产线 工厂模式
查看>>
浅谈javascript异步发展历程
查看>>
在vscode使用editorconfig的正确姿势
查看>>
当心!你的密码正在被手机中的声音泄露!
查看>>
你用过 PropTypes 的这些类型检查么?
查看>>
H5小游戏 【篇一】 组词游戏
查看>>