基于Apache Shrio的微服务认证授权方案

2023-09-14 14:21:46辽河石油勘探局有限公司信息工程分公司林哲
数字技术与应用 2023年2期
关键词:令牌网关单体

辽河石油勘探局有限公司信息工程分公司 林哲

信息化的高速发展推动了计算机应用架构的迭代演进,传统单体应用项目的认证与授权方式已不适用于基于微服务架构的系统应用。本文提出了一种微服务架构下的认证授权方案,基于Apache Shrio整合Oauth2.0协议,实现独立的微服务认证与授权中心;整合JWT作为认证令牌,增强系统的安全性、易用性。对于原有项目升级改造以及微服务应用构建开发均具备一定的参考价值。

随着网络技术的发展、应用规模的不断扩大,以及应用场景的日趋复杂化,计算机应用架构也在不断演变,逐渐从单体应用架构演化出了微服务架构等更加有利于资源匹配的架构形式。随之产生的问题是单体架构中的认证授权机制不能直接应用到微服务架构中,需要重新设计一种认证授权方案,使其符合微服务无状态、资源分散、技术多样性等特点。Apache Shrio作为单体应用中的主流安全框架,是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能,可以为任何应用提供安全保障[1]。Shiro提供的接口使其具有灵活的扩展能力,具备扩展为微服务架构安全框架的可能性。Oauth2.0协议的出现则为开发人员提供了一整套适用于微服务架构的认证授权开发标准。本文基于上述技术,研究了单体项目和微服务认证授权的机制,提出一种微服务架构下的认证与授权方案。

1 技术路线

1.1 微服务

微服务是一种软件架构方案,通常由一组职责单一、具备自治性的独立服务组成。服务之间由服务注册、服务发现、网关、链路追踪、熔断机制等技术进行管理、维护。相较于单体项目,微服务架构具备以下优势:

(1)弹性配置。微服务之间通过一些轻量级的通信机制进行通信,由于服务本身及网络情况的不确定性,通信间的交互可能出现故障。由于微服务架构中的服务通常具备独立职责且结构完整,而微服务应用程序的弹性也主要取决于微服务通信的可靠性。因此微服务提供了许多机制来保证微服务应用程序的弹性,其中包括超时、重试、断路器、快速故障、隔板、事务、负载平衡,故障转移和保证的交付等。(2)独立扩展。在传统单体项目中,对单一服务进行拓展时,由于系统间的服务耦合度高,导致系统整体也会受到影响。而微服务体系是由一组相对独立的服务组成,可以指向性地针对特定服务进行单独扩展,这样就能根据实际需求合理分配资源到不同的服务中去,从而达到最佳的性能效果。(3)简化部署。单体项目改版发布的难度随其代码规模的增长而增长,任何变动都需要整个项目重新部署。对于大体量项目来说,项目的重新部署以及处理可能产生的BUG都需要很高的成本,且具有很高的风险度。微服务由于互相之间相对独立,单一微服务体量小易部署,即使产生BUG,也能够快速回滚到之前的稳定版本进行部署,对当前运行的其他程序影响不大。

1.2 Apache Shiro

Apache Shiro是一种功能强大且易于使用的Java安全框架,它具有身份验证、访问授权、数据加密、会话管理等功能,可用于保护任何应用程序的安全[2]。Shiro主要由3个组件构成,分别为主体(Subject)、安全管理器(Security Manager)和领域(Realm)。

(1)主体。Subject是一个抽象概念,表示与程序交互的对象,是Shiro框架对外的核心对象。代表当前“用户”,“用户”一次不一定指的是人,也可以是其他应用程序、接口等。Subject作为对外接口,其中的交互操作实际都是由Security Manager来完成。(2)安全管理器。Security Manager是Shiro框架的核心组件。所有涉及系统安全的相关操作都会与Security Manager进行交互,Security Manager负责管理Shiro中的其他组件,协调各组件共同完成安全管理任务。(3)领域。Realm是安全信息数据源,内部封装了通用数据源链接,用于作为安全验证的参考数据。当进行授权、令牌获取、令牌认证等操作时,Shiro通过配置的Realm查找相关的数据信息,Realm可以配置一个或多个。当Shrio默认提供的Realm无法满足需求时,可以自定义领域实现,形成定制化数据源。

1.3 JWT

Json Web Token (JWT)是一个基于 RFC7519 的信息传递协议,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准[3]。JWT是由点符号连接的3个部分组成,分别为标头(Header)、载荷(Payload)和签名(Signature),其中Header存储了所使用的加密算法和Token类型;Payload是一个JSON对象,作为JWT的主体部分,官方规定了7个字段供开发者选用,也可以自定义字段和内容;Signature是对前2个部分的签名,作用是防止数据篡改。这3个部分分别单独进行Base64编码后使用点符号拼接成完整的JWT。相比传统的Session认证方式,基于Token的认证方式更适用于移动端和分布式,且更节约计算资源,具备支持跨域访问、无状态、适用于CDN、无需考虑CSRF等特性。

1.4 Oauth2.0

OAuth2.0是一个授权第三方服务访问Web项目的安全协议。OAuth2.0将第三方应用与用户的安全信息进行分隔,并且提供简单、标准的实现方案来访问应用中受保护的资源。该协议具备开放、灵活、简单、安全等特点。OAuth2.0采用无状态的认证授权方式,以令牌作为安全信息的载体,适用于多服务独立部署的微服务架构。令牌基于Rest风格的API在服务间进行传递。

1.5 Apache Oltu

Apache Oltu是OAuth2.0协议基于Java语言的实现,具有轻量、简单、灵活等特点。Apache Oltu源码包分为4个部分:

(1)Issuer:生成授权码和访问令牌,刷新令牌;(2)Request:封装授权码请求和令牌请求的逻辑,并提供相应的校验服务;(3)Response:封装授权流程中的响应逻辑,提供生成不同响应结果的方法;(4)Validator:为Request提供校验服务。

2 方案设计

(1)微服务入口处理。微服务架构通常由API网关作为系统对外的唯一入口。因此网关可以整合令牌的初步认证。通过获取请求中的令牌信息,并对信息进行解析、校验等操作,实现身份认证以及部分授权功能。另外,网关需要设置白名单,即验证码、令牌获取等请求不需要进行身份认证即可访问。(2)获取授权码。当外部应用对服务发起授权码请求时,网关通过判断请求在白名单列表中,直接将请求转发至认证中心。认证中心对用户名密码进行验证,验证通过后生成授权码,并以重定向URL参数的形式返回给用户。(3)令牌的选择及获取。方案采取无状态认证授权方式,使用Token作为安全信息载体。为提高令牌的安全性、易用性、灵活性,采用JWT作为令牌实现。经由网关白名单释放的令牌获取请求到达认证中心。认证中心对请求中携带的授权码验证,验证通过后生成验证码。(4)资源授权。当请求携带有效Token访问服务时,网关通过对Token的解析,获取身份信息及权限信息。并将信息重新加密后通过请求传递给资源中心,资源中心解密信息后,将信息存入安全上下文。资源中心根据上下文中的权限信息对请求进行授权并获取资源,最终完成请求访问流程。

3 功能实现

3.1 网关整合令牌认证

通过实现Spring Cloud Gateway的GlobalFilter实现对网关访问请求的拦截,从中获取令牌,并使用JWTStore进行解析,获取权限信息及身份信息并进行身份认证。认证通过后,将安全信息编码后添加到请求,通过Filter Chain向后传递。

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

String requestUrl = exchange.getRequest().getPath().value();

AntPathMatcher pathMatcher = new AntPath Matcher();

//1 认证中心服务所有放行

if (pathMatcher.match("/sso-server/**",requestUrl)) {

return chain.filter(exchange);

}

//2 检查token是否存在

String token = getToken(exchange);

if (!StringUtils.hasLength(token)) {

return noTokenMono(exchange);

}

//3 判断是否是有效的token

OAuth2AccessToken oAuth2AccessToken;

try {

oAuth2AccessToken = tokenStore.readAccess Token(token);

Map<String, Object> additionalInformation =oAuth2AccessToken.getAdditionalInformation();

//取出用户身份信息

String principal = (String)additionalInformation.get("user_name");

//获取用户权限

List<String> authorities = (List<String>)additionalInformation.get("authorities");

HashMap hashMap = new HashMap();

hashMap.put("principal",principal);

hashMap.put("authorities",authorities);

//给header添加值

String base64 = "";

try {

base64 = EncryptUtil.encodeUTF8StringBase64(new ObjectMapper().writeValueAsString(hashMap));

} catch (IOException e) {

e.printStackTrace();

}

ServerHttpRequest tokenRequest = exchange.get Request().mutate().header("json-token", base64).build();

ServerWebExchange build = exchange.mutate().request(tokenRequest).build();

return chain.filter(build);

} catch (InvalidTokenException e) {

return invalidTokenMono(exchange);

}

}

3.2 获取授权码

授权码获取采用Shiro整合Oltu实现。通过Subject状态判断用户是否已登录,如果通过状态判定,则生成授权码,并以重定向方式将授权码返回给客户端。

//封装OAth请求

OAuthAuthzRequest authzRequest=new OAuth AuthzRequest(request);

//检查client_id

if(!checkClientId(authzRequest.getClientId())){

//错误则构造错误响应

return "error";

}

//判断用户登录状态

Subject subject=SecurityUtils.getSubject();

if(!subject.isAuthenticated())

return "oauth2login";

}

//判断状态为登录成功,则生成授权码code

String username=(String)subject.getPrincipal();

String authorizCode=null;

String responseType= authzRequest

.getParam("response_type");

if(responseType.equals("code")){

//生成authCode

OAuthIssuerImpl issuerImpl=new OAuthIssuerImpl(

new MD5Generator());

authorizCode= issuerImpl.authorizationCode();

addAuthCode(authorizCode,username);

}

//重定向回客户端地址

String redURI=authzRequest

.getParam("redirectUrl");

final OAuthResponse response= OAuthASResponse

.authorizationResponse(request,302)

.setCode(authorizCode)

.location(redURI)

.buildQueryMessage();

HttpHeaders hs=new HttpHeaders();

URI uri = new URI(response.getLocationUri());

hs.setLocation(uri);

return new ResponseEntity(hs, HttpStatus.FOUND);

3.3 获取令牌

Shiro的领域接口提供了Supports方法,用于设置Realm支持的Token类型。重写Supports方法,指定Token验证类型为封装的JWT类型,为整合JWT做准备。通过对自定义Realm中的认证方法进行重写,增加令牌认证功能。

public class JwtShiroRealm extends AuthorizingRealm{

@Override

public boolean supports(AuthenticationToken token) {

return token instanceof JwtToken;

}

// 认证

@Override

protected AuthenticationInfo doGetAuthenticationInfo(Au thenticationToken token) throws AuthenticationException {

String credentials = (String) token.getCredentials();

// 解密获得username,用于和数据库进行对比

String username = JwtUtil.getUsername(credentials);

if ("admin".equals(username)) {

//数据库查出来的用户

User user = getUser();

//验证密码是否正确

if (JwtUtil.verify(tokenStr, username, user.get Password())) {

log.info("登录成功");

} else {

throw new UnknownAccountException("用户名密码错误");

}

SimpleAuthenticationInfo simpleAuthenticationInfo= new SimpleAuthenticationInfo(token.getCredentials(),token.getCredentials(), this.getName());

return simpleAuthenticationInfo;

}

return null;

}

}

3.4 整合JWT实现

自定义JWT工具类,实现生成令牌、令牌验证、获取对应信息等相关功能。

public class JwtUtil {

private static final long EXPIRE_TIME = 60 *60 * 1000;

//校验token

public static boolean check(String token, String name, String secret) {

try {

//根据秘钥生成JWT效验器

Algorithm alg = Algorithm.HMAC256(secret);

JWTVerifier verif = JWT.require(alg)

.withClaim("username", name)

.build();

//校验token

DecodedJWT dcjwt = verif.verify(token);

return true;

} catch (Exception e) {

return false;

}

}

//获取用户名

public static String getUsername(String token) {

try {

DecodedJWT jwt = JWT.decode(token);

return jwt.getClaim("username").asString();

} catch (JWTDecodeException e) {

return null;

}

}

//生成token

public static String sign(String uname, String secret) {

return JWT.create()

.withJWTId(UUID.randomUUID().toString())

.withClaim("username", uname)

.withExpiresAt(new Date(getCurrentDate()))

.sign(Algorithm.HMAC256(secret));

}

}

4 结语

本文通过对Apache Shiro、JWT、微服务架构等技术的介绍与分析,提出了一种基于Apache Shiro的微服务认证授权方案。方案在保留Shiro原有特性的前提下,实现了对微服务架构的适配,具有高效率、高安全性、低耦合度、可扩展等特点。方案所实现的安全框架能够完成认证授权在微服务架构中的完全覆盖,同时也保留了一定的灵活性,读者可以根据实际情况调整安全框架的有效范围,从而找到应用整体的最佳运行效果。

引用

[1]时子庆,刘金兰,谭晓华.基于OAuth2.0的认证授权技术[J].计算机系统应用,2012,21(3):260-264.

[2]梁清华,胡安明.Apache Shiro框架在Web系统的安全应用研究[J].电脑知识与技术,2021,17(6):52-53.

[3]范展源,罗福强.JWT认证技术及其在WEB中的应用[J].数字技术与应用,2016(2):114.

猜你喜欢
令牌网关单体
称金块
基于改进RPS技术的IPSEC VPN网关设计
基于路由和QoS令牌桶的集中式限速网关
动态令牌分配的TCSN多级令牌桶流量监管算法
计算机工程(2018年8期)2018-08-17 00:26:54
单体光电产品检验验收方案问题探讨
中国军转民(2017年7期)2017-12-19 13:30:00
相变大单体MPEGMA的制备与性能
LTE Small Cell网关及虚拟网关技术研究
移动通信(2015年18期)2015-08-24 07:45:08
应对气候变化需要打通“网关”
太阳能(2015年7期)2015-04-12 06:49:50
巨无霸式医疗单体的选择
中国卫生(2014年10期)2014-11-12 13:10:24
类姜黄素及其单体对β-内分泌酶活性的抑制作用