实现简单微服务单点登录(SpringSecurity+Gateway+Redis)
一、需求整个微服务项目有两个服务,一个用户鉴权服务和业务处理服务。基本需求是用户通过鉴权服务进行用户认证操作并返回令牌Token,后续通过token访问业务服务。二、项目基本结构- gateway- service- service-user- service-businessservice-user是用户鉴权服务,主要采用SpringSecurity实现用户鉴权。service-business
·
一、需求
整个微服务项目有两个服务,一个用户鉴权服务和业务处理服务。基本需求是用户通过鉴权服务进行用户认证操作并返回令牌Token,后续通过token访问业务服务。
二、项目基本结构
- gateway
- service
- service-user
- service-business
service-user是用户鉴权服务,主要采用SpringSecurity实现用户鉴权。service-business则是一些业务操作。
gateway主要用于服务分发
三、鉴权服务
用户鉴权服务实现的主要思路是用户登录验证成功后生成token,并将其存放在redis中。
SpringSecurity的相关使用就不多赘述,网上有很多教程
用户认证成功后调用的方法
//2 认证成功调用的方法
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
System.out.println("认证成功...");
//认证成功,得到认证成功之后用户信息
SecurityUser user = (SecurityUser)authResult.getPrincipal();
//把用户名称和用户权限列表放到redis
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getNickName(),user.getPermissionValueList());
ResponseUtil.out(response, Result.ok(userService.loginSuccess(user,request,tokenManager)));
}
userService.java
@Override
public User loginSuccess(SecurityUser user, HttpServletRequest request, TokenManager tokenManager) {
//根据用户名生成token
String token = tokenManager.createToken(user.getCurrentUserInfo().getNickName());
System.out.println("认证成功后生成的用户token:"+token);
// 记录用户登录日志
User userInfo = user.getCurrentUserInfo();
String ipAddr = NetworkUtil.getIpAddr(request);
userLoginRecordService.userLoginRecord(userInfo.getId(),ipAddr);
//返回token
userInfo.setToken(token);
//清空密码
userInfo.setPassword(null);
//设置权限列表
userInfo.setPermissionList(user.getPermissionValueList());
return userInfo;
}
在登录成功的方法里可以进行一系列操作(比如记录用户登录日志),将用户信息及生成的token返回给前端
四、网关
前端请求头中带上鉴权成功返回的token访问网关
AuthGlobalFilter.java
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private TokenManager tokenManager = new TokenManager();
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println("==="+path);
//登录和注册请求直接放行
if(antPathMatcher.match("/api/user/login", path) ||
antPathMatcher.match("/api/user/register", path)) {
return chain.filter(exchange);
}
//api接口,异步请求,校验用户必须登录
if(antPathMatcher.match("/api/**", path)) {
String userName = this.getUserName(request);
System.out.println("获取登录用户信息:"+userName);
if(StringUtils.isEmpty(userName)) {
ServerHttpResponse response = exchange.getResponse();
return out(response, ResultCodeEnum.LOGIN_AUTH);
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
/**
* api接口鉴权失败返回数据
* @param response
* @return
*/
private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
Result result = Result.build(null, resultCodeEnum);
byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
/**
* 获取当前登录用户信息
* @param request
* @return
*/
private String getUserName(ServerHttpRequest request) {
String token = "";
List<String> tokenList = request.getHeaders().get("token");
if(null != tokenList) {
token = tokenList.get(0);
}
System.out.println("获取到的token:"+token);
if(!StringUtils.isEmpty(token)) {
// 通过获取到的token获取用户名并检查redis中是否保留登录信息
// TODO
// 如果redis中不存在用户登录信息则直接返回null
return tokenManager.getUserInfoFromToken(token);
}
return null;
}
}
网关通过解析token获取用户名,然后通过用户名查询redis获取用户权限,这样就能进行下一步操作了。
更多推荐
已为社区贡献2条内容
所有评论(0)