用户授权业务流程
业务流程说明
(1)用户认证通过,认证服务向浏览器cookie写入token(身份令牌) (2)前端携带token请求用户中心服务获取jwt令牌
前端获取到jwt令牌解析,并存储在sessionStorage (3)前端携带cookie中的身份令牌及jwt令牌访问资源服务
前端请求资源服务需要携带两个token:一个是cookie中的身份令牌,一个是http header中的jwt 前端请求资源服务前在http header上添加jwt请求资源 网关验证token的合法性
用户请求必须携带身份令牌和jwt令牌 网关校验redis中user_token的有效期,已过期则要求用户重新登录 资源服务校验jwt的合法性并进行授权
资源服务校验jwt令牌,完成授权,拥有权限的方法正向执行,没有权限的方法拒绝访问。
方法授权
需求分析
生成jwt令牌时动态查询用户拥有的权限
管理员给用户分配权限,权限数据写到数据库中。 、认证服务在进行用户认证时从数据库读取用户的权限数据(动态数据) 资源服务方法添加注解PreAuthorize,指定方法所需要的权限
@PreAuthorize("hasAuthority('course_find_list')")
@Override
public QueryResult<CourseInfo> findCourseList(@PathVariable("page") int page,
@PathVariable("size") int size, CourseListRequest courseListRequest)
当请求有权限的方法时正常访问 当请求没有权限的方法时拒绝访问
jwt令牌包含权限
修改认证服务的UserDetailServiceImpl类 将权限标识按照中间使用逗号分隔的语法组成一个字符串,最终提供给Spring security。
//指定用户的权限,这里暂时硬编码
List<String> permissionList = new ArrayList<>();
permissionList.add("course_get_baseinfo");
permissionList.add("course_find_pic");
//将权限串中间以逗号分隔
String permissionString = StringUtils.join(permissionList.toArray(), ",");
//String user_permission_string = "";
UserJwt userDetails = new UserJwt(username,
password,AuthorityUtils.commaSeparatedStringToAuthorityList(permissionString));
方法授权实现
细粒度授权
分析
细粒度授权也叫数据范围授权,即不同的用户所拥有的操作权限相同,但是能够操作的数据范围是不一样的。
实现
细粒度授权涉及到不同的业务逻辑,通常在service层实现,根据不同的用户进行校验,根据不同的参数查询不同的数据或操作不同的数据。 获取当前用户信息
认证服务在用户认证通过将用户需要的信息存储到jwt令牌中。 用户请求到达资源服务后,资源服务需要取出header中的jwt令牌,并解析出用户信息。 JWT解析工具类
public class Oauth2Util {
public static Map<String,String> getJwtClaimsFromHeader(HttpServletRequest request) {
if (request == null) {
return null;
}
//取出头信息
String authorization = request.getHeader("Authorization");
if (StringUtils.isEmpty(authorization) || authorization.indexOf("Bearer") < 0) {
return null;
}
//从Bearer 后边开始取出token
String token = authorization.substring(7);
Map<String,String> map = null;
try {
//解析jwt
Jwt decode = JwtHelper.decode(token);
//得到 jwt中的用户信息
String claims = decode.getClaims();
//将jwt转为Map
map = JSON.parseObject(claims, Map.class);
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}
微服务之间认证
分析
用户携带身份令牌和JWT令牌访问微服务,微服务获取jwt并完成授权。 当微服务访问微服务,此时如果没有携带JWT则微服务会在授权时报错。
Feign拦截
定义Feign拦截器
微服务之间使用feign进行远程调用,采用feign拦截器实现远程调用携带JWT。 public class FeignClientInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
try {
//使用RequestContextHolder工具获取request相关变量
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
if(attributes!=null){
//取出request
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
if(name.equals("authorization")){
//System.out.println("name="+name+"values="+values);
requestTemplate.header(name, values);
}
}
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
@Bean
public FeignClientInterceptor feignClientInterceptor(){
return new FeignClientInterceptor();
}