# 结论:认证服务应用的话,是没有验证码的处理的。
# 关于验证码的处理,是在网关这边就处理完了。
spring:
redis:
host: localhost
port: 6379
password:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
- id: ruoyi-auth
uri: lb://ruoyi-auth
# 第二步:调用后端的认证中心接口
predicates:
- Path=/auth/**
filters:
# 第一步:校验验证码
- CacheRequestFilter
- ValidateCodeFilter
- StripPrefix=1
# Tomcat
server:
port: 9200
# Spring
spring:
application:
# 应用名称
name: ruoyi-auth
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 127.0.0.1:8848
config:
# 配置中心地址
server-addr: 127.0.0.1:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
com.ruoyi
ruoyi-common-security
/**
* 认证授权中心
*/
/**
* 自定义的ruoyi-security模块提供的注解,
* 会去加载Feign的注解,
*
*/
@EnableRyFeignClients
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class RuoYiAuthApplication
{
public static void main(String[] args)
{
SpringApplication.run(RuoYiAuthApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 认证授权中心启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}
提示:
目前已经存在
ruoyi-auth
认证授权中心,用于登录认证,系统退出,刷新令牌。
/**
* 自定义feign注解
* 添加basePackages路径
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableRyFeignClients
{
String[] value() default {};
/**
* 扫描"com.ruoyi"包及其子孙所下面的所有feign注解,
* 给它注入进来,不然我们就会实现不了服务之间的相互调用了。
*/
String[] basePackages() default { "com.ruoyi" };
Class>[] basePackageClasses() default {};
Class>[] defaultConfiguration() default {};
Class>[] clients() default {};
}
@PostMapping("login")
// LoginBody用户对象,包含了username、password
public R> login(@RequestBody LoginBody form)
{
// 用户登录
LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());
// 获取登录token
return R.ok(tokenService.createToken(userInfo));
}
/**
* 登录
*/
public LoginUser login(String username, String password)
{
// 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
throw new ServiceException("用户/密码必须填写");
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
throw new ServiceException("用户密码不在指定范围");
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
throw new ServiceException("用户名不在指定范围");
}
// IP黑名单校验
String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单");
throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
}
/**
* openfeign(RPC)实现服务之间的相互调用,查询数据库获取LoginUser对象信息。
* LoginUser包含:用户令牌、用户的角色集合、用户的权限集合。
*/
R userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
throw new ServiceException("登录用户:" + username + " 不存在");
}
if (R.FAIL == userResult.getCode())
{
throw new ServiceException(userResult.getMsg());
}
LoginUser userInfo = userResult.getData();
SysUser user = userResult.getData().getSysUser();
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
}
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
}
/**
* 密码校验
*/
passwordService.validate(user, password);
/**
* 记录登录日志
*/
recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
return userInfo;
}
/**
* 获取当前用户信息
*/
@InnerAuth
@GetMapping("/info/{username}")
public R info(@PathVariable("username") String username)
{
SysUser sysUser = userService.selectUserByUserName(username);
if (StringUtils.isNull(sysUser))
{
return R.fail("用户名或密码错误");
}
// 角色集合
Set roles = permissionService.getRolePermission(sysUser);
// 权限集合
Set permissions = permissionService.getMenuPermission(sysUser);
LoginUser sysUserVo = new LoginUser();
sysUserVo.setSysUser(sysUser);
sysUserVo.setRoles(roles);
sysUserVo.setPermissions(permissions);
return R.ok(sysUserVo);
}
/**
* 创建令牌
*/
public Map createToken(LoginUser loginUser)
{
String token = IdUtils.fastUUID();
Long userId = loginUser.getSysUser().getUserId();
String userName = loginUser.getSysUser().getUserName();
loginUser.setToken(token);
loginUser.setUserid(userId);
loginUser.setUsername(userName);
loginUser.setIpaddr(IpUtils.getIpAddr());
/**
* redis缓存中数据要刷新一下
*/
refreshToken(loginUser);
// Jwt存储信息
Map claimsMap = new HashMap();
claimsMap.put(SecurityConstants.USER_KEY, token);
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
// 接口返回信息
Map rspMap = new HashMap();
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
rspMap.put("expires_in", expireTime);
return rspMap;
}
/**
* 验证令牌有效期,相差不足120分钟,自动刷新缓存
*
* @param loginUser
*/
public void verifyToken(LoginUser loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
{
refreshToken(loginUser);
}
}
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
/**
* 往redis中保存数据
*/
redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
/**
* 权限缓存前缀
*/
public final static String LOGIN_TOKEN_KEY = "login_tokens:";
用户登录接口地址 http://localhost:9200/login
请求头Content-Type - application/json
,请求方式Post
{
"username": "admin",
"password": "admin123"
}
响应结果
{
"code": 200,
"data": {
"access_token": "f840488c-68a9-4272-acc9-c34d3b66a943",
"expires_in": 43200
}
}
通过用户验证登录后获取access_token
,通过网关访问其他应用数据时必须携带此参数值。
@PostMapping("refresh")
/**
* request必须包含token
*/
public R> refresh(HttpServletRequest request)
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser))
{
// 刷新令牌有效期
tokenService.refreshToken(loginUser);
return R.ok();
}
return R.ok();
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request)
{
// 获取请求携带的令牌
String token = SecurityUtils.getToken(request);
return getLoginUser(token);
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(String token)
{
LoginUser user = null;
try
{
if (StringUtils.isNotEmpty(token))
{
String userkey = JwtUtils.getUserKey(token);
/**
* 从缓存中获取LoginUser
*/
user = redisService.getCacheObject(getTokenKey(userkey));
return user;
}
}
catch (Exception e)
{
log.error("获取用户信息异常'{}'", e.getMessage());
}
return user;
}
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
/**
* 往redis中保存数据
*/
redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
顾名思义,就是对系统操作用户的进行缓存刷新,防止过期。
TokenController
控制器refresh
方法会在用户调用时更新令牌有效期。
刷新令牌接口地址 http://localhost:9200/refresh
请求头Authorization - f840488c-68a9-4272-acc9-c34d3b66a943
,请求方式Post
响应结果
{
"code": 200,
}
刷新后有效期为默720(分钟)。
顾名思义,就是对系统登用户的退出过程。
TokenController
控制器logout
方法会在用户退出时删除缓存信息同时保存用户退出日志。源码ruoyi-auth#TokenController.logout:
@DeleteMapping("logout")
public R> logout(HttpServletRequest request)
{
String token = SecurityUtils.getToken(request);
if (StringUtils.isNotEmpty(token))
{
String username = JwtUtils.getUserName(token);
// 删除用户缓存记录
AuthUtil.logoutByToken(token);
// 记录用户退出日志
sysLoginService.logout(username);
}
return R.ok();
}
系统退出接口地址 http://localhost:9200/logout
请求头Authorization - f840488c-68a9-4272-acc9-c34d3b66a943
,请求方式Delete
{
"username": "admin",
"password": "admin123"
}
响应结果
{
"code": 200,
}