Commit debfa697 by giaogiao

shiro的token校验+redis

parent 4e79ae21
......@@ -21,14 +21,10 @@ import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.config.properties.ShiroPermissionProperties;
import io.geekidea.springbootplus.config.properties.ShiroProperties;
import io.geekidea.springbootplus.framework.shiro.cache.AppLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.cache.MerchantLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.exception.ShiroConfigException;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtCredentialsMatcher;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtFilter;
import io.geekidea.springbootplus.framework.shiro.jwt.realm.JwtRealmAppUser;
import io.geekidea.springbootplus.framework.shiro.jwt.realm.JwtRealmMerchant;
import io.geekidea.springbootplus.framework.shiro.jwt.realm.JwtRealmSystem;
import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
import io.geekidea.springbootplus.framework.util.IniUtil;
import lombok.extern.slf4j.Slf4j;
......@@ -108,20 +104,6 @@ public class ShiroConfig {
}
/**
* 系统的用户及权限数据
*
* @return
*/
@Bean
public JwtRealmSystem jwtRealmSystem(SysLoginRedisService loginRedisService) {
JwtRealmSystem jwtRealm = new JwtRealmSystem(loginRedisService);
jwtRealm.setCachingEnabled(false);
jwtRealm.setCredentialsMatcher(credentialsMatcher());
return jwtRealm;
}
/**
* app的用户及权限数据
*/
@Bean
......@@ -134,18 +116,6 @@ public class ShiroConfig {
/**
* 商家的用户及权限数据
*/
@Bean
public JwtRealmMerchant jwtRealmMerchant(MerchantLoginRedisService merchantLoginRedisService) {
JwtRealmMerchant jwtRealm = new JwtRealmMerchant(merchantLoginRedisService);
jwtRealm.setCachingEnabled(false);
jwtRealm.setCredentialsMatcher(credentialsMatcher());
return jwtRealm;
}
/**
* subject不存储到Session中
*/
@Bean
......@@ -171,16 +141,16 @@ public class ShiroConfig {
* 安全管理器配置
*/
@Bean
public SecurityManager securityManager(SysLoginRedisService sysLoginRedisService, AppLoginRedisService appLoginRedisService, MerchantLoginRedisService merchantLoginRedisService) {
public SecurityManager securityManager(AppLoginRedisService appLoginRedisService) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置多个realms
List<Realm> realms = new ArrayList<>();
realms.add(jwtRealmSystem(sysLoginRedisService));
// realms.add(jwtRealmSystem(sysLoginRedisService));
realms.add(jwtRealmAppUser(appLoginRedisService));
realms.add(jwtRealmMerchant(merchantLoginRedisService));
// realms.add(jwtRealmMerchant(merchantLoginRedisService));
// 数据连接器
// 用户数据连接器
securityManager.setRealms(realms);
// 设置session存储方式: 不使用sessionId保存
......@@ -193,7 +163,6 @@ public class ShiroConfig {
* ShiroFilterFactoryBean过滤器配置
*
* @param securityManager
* @param sysLoginRedisService
* @param shiroProperties
* @param jwtProperties
* @return
......@@ -201,7 +170,6 @@ public class ShiroConfig {
@Bean(SHIRO_FILTER_NAME)
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
ShiroLoginService shiroLoginService,
SysLoginRedisService sysLoginRedisService,
ShiroProperties shiroProperties,
JwtProperties jwtProperties) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
......@@ -209,7 +177,7 @@ public class ShiroConfig {
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置过滤器
Map<String, Filter> filterMap = getFilterMap(shiroLoginService, jwtProperties);
Map<String, Filter> filterMap = getFilterMap(shiroLoginService);
shiroFilterFactoryBean.setFilters(filterMap);
// 设置过滤器顺序
Map<String, String> filterChainMap = getFilterChainDefinitionMap(shiroProperties);
......@@ -223,10 +191,9 @@ public class ShiroConfig {
*
* @return
*/
private Map<String, Filter> getFilterMap(ShiroLoginService shiroLoginService,
JwtProperties jwtProperties) {
private Map<String, Filter> getFilterMap(ShiroLoginService shiroLoginService) {
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put(JWT_FILTER_NAME, new JwtFilter(shiroLoginService, jwtProperties));
filterMap.put(JWT_FILTER_NAME, new JwtFilter(shiroLoginService));
return filterMap;
}
......@@ -346,14 +313,12 @@ public class ShiroConfig {
/**
* 认证模式
*
* @param sysLoginRedisService
* @return
*/
@Bean
public Authenticator authenticator(SysLoginRedisService sysLoginRedisService) {
public Authenticator authenticator() {
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
List<Realm> realms = new ArrayList<>();
realms.add(jwtRealmSystem(sysLoginRedisService));
authenticator.setRealms(realms);
// 认证策略: 第一个成功
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
......
package com.wecloud.im.controller;
import com.wecloud.im.entity.ImConversation;
import com.wecloud.im.param.ImConversationPageParam;
import com.wecloud.im.param.ImConversationQueryVo;
import com.wecloud.im.service.ImConversationService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.core.validator.groups.Add;
import io.geekidea.springbootplus.framework.core.validator.groups.Update;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
......@@ -16,8 +12,6 @@ import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -49,49 +43,49 @@ public class ImConversationController extends BaseController {
return ApiResult.result(flag);
}
/**
* 修改会话表
*/
@PostMapping("/update")
@OperationLog(name = "修改会话表", type = OperationLogType.UPDATE)
@ApiOperation(value = "修改会话表")
public ApiResult<Boolean> updateImConversation(@Validated(Update.class) @RequestBody ImConversation imConversation) throws Exception {
boolean flag = imConversationService.updateImConversation(imConversation);
return ApiResult.result(flag);
}
/**
* 删除会话表
*/
@PostMapping("/delete/{id}")
@OperationLog(name = "删除会话表", type = OperationLogType.DELETE)
@ApiOperation(value = "删除会话表")
public ApiResult<Boolean> deleteImConversation(@PathVariable("id") Long id) throws Exception {
boolean flag = imConversationService.deleteImConversation(id);
return ApiResult.result(flag);
}
/**
* 获取会话表详情
*/
@GetMapping("/info/{id}")
@OperationLog(name = "会话表详情", type = OperationLogType.INFO)
@ApiOperation(value = "会话表详情")
public ApiResult<ImConversationQueryVo> getImConversation(@PathVariable("id") Long id) throws Exception {
ImConversationQueryVo imConversationQueryVo = imConversationService.getImConversationById(id);
return ApiResult.ok(imConversationQueryVo);
}
/**
* 会话表分页列表
*/
@PostMapping("/getPageList")
@OperationLog(name = "会话表分页列表", type = OperationLogType.PAGE)
@ApiOperation(value = "会话表分页列表")
public ApiResult<Paging<ImConversationQueryVo>> getImConversationPageList(@Validated @RequestBody ImConversationPageParam imConversationPageParam) throws Exception {
Paging<ImConversationQueryVo> paging = imConversationService.getImConversationPageList(imConversationPageParam);
return ApiResult.ok(paging);
}
// /**
// * 修改会话表
// */
// @PostMapping("/update")
// @OperationLog(name = "修改会话表", type = OperationLogType.UPDATE)
// @ApiOperation(value = "修改会话表")
// public ApiResult<Boolean> updateImConversation(@Validated(Update.class) @RequestBody ImConversation imConversation) throws Exception {
// boolean flag = imConversationService.updateImConversation(imConversation);
// return ApiResult.result(flag);
// }
//
// /**
// * 删除会话表
// */
// @PostMapping("/delete/{id}")
// @OperationLog(name = "删除会话表", type = OperationLogType.DELETE)
// @ApiOperation(value = "删除会话表")
// public ApiResult<Boolean> deleteImConversation(@PathVariable("id") Long id) throws Exception {
// boolean flag = imConversationService.deleteImConversation(id);
// return ApiResult.result(flag);
// }
//
// /**
// * 获取会话表详情
// */
// @GetMapping("/info/{id}")
// @OperationLog(name = "会话表详情", type = OperationLogType.INFO)
// @ApiOperation(value = "会话表详情")
// public ApiResult<ImConversationQueryVo> getImConversation(@PathVariable("id") Long id) throws Exception {
// ImConversationQueryVo imConversationQueryVo = imConversationService.getImConversationById(id);
// return ApiResult.ok(imConversationQueryVo);
// }
//
// /**
// * 会话表分页列表
// */
// @PostMapping("/getPageList")
// @OperationLog(name = "会话表分页列表", type = OperationLogType.PAGE)
// @ApiOperation(value = "会话表分页列表")
// public ApiResult<Paging<ImConversationQueryVo>> getImConversationPageList(@Validated @RequestBody ImConversationPageParam imConversationPageParam) throws Exception {
// Paging<ImConversationQueryVo> paging = imConversationService.getImConversationPageList(imConversationPageParam);
// return ApiResult.ok(paging);
// }
}
package com.wecloud.im.controller;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.param.ImConversationMembersPageParam;
import com.wecloud.im.param.ImConversationMembersQueryVo;
import com.wecloud.im.service.ImConversationMembersService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.core.validator.groups.Add;
import io.geekidea.springbootplus.framework.core.validator.groups.Update;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
......@@ -16,8 +12,6 @@ import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -48,50 +42,50 @@ public class ImConversationMembersController extends BaseController {
boolean flag = imConversationMembersService.saveImConversationMembers(imConversationMembers);
return ApiResult.result(flag);
}
/**
* 修改会话成员表
*/
@PostMapping("/update")
@OperationLog(name = "修改会话成员表", type = OperationLogType.UPDATE)
@ApiOperation(value = "修改会话成员表")
public ApiResult<Boolean> updateImConversationMembers(@Validated(Update.class) @RequestBody ImConversationMembers imConversationMembers) throws Exception {
boolean flag = imConversationMembersService.updateImConversationMembers(imConversationMembers);
return ApiResult.result(flag);
}
/**
* 删除会话成员表
*/
@PostMapping("/delete/{id}")
@OperationLog(name = "删除会话成员表", type = OperationLogType.DELETE)
@ApiOperation(value = "删除会话成员表")
public ApiResult<Boolean> deleteImConversationMembers(@PathVariable("id") Long id) throws Exception {
boolean flag = imConversationMembersService.deleteImConversationMembers(id);
return ApiResult.result(flag);
}
/**
* 获取会话成员表详情
*/
@GetMapping("/info/{id}")
@OperationLog(name = "会话成员表详情", type = OperationLogType.INFO)
@ApiOperation(value = "会话成员表详情")
public ApiResult<ImConversationMembersQueryVo> getImConversationMembers(@PathVariable("id") Long id) throws Exception {
ImConversationMembersQueryVo imConversationMembersQueryVo = imConversationMembersService.getImConversationMembersById(id);
return ApiResult.ok(imConversationMembersQueryVo);
}
/**
* 会话成员表分页列表
*/
@PostMapping("/getPageList")
@OperationLog(name = "会话成员表分页列表", type = OperationLogType.PAGE)
@ApiOperation(value = "会话成员表分页列表")
public ApiResult<Paging<ImConversationMembersQueryVo>> getImConversationMembersPageList(@Validated @RequestBody ImConversationMembersPageParam imConversationMembersPageParam) throws Exception {
Paging<ImConversationMembersQueryVo> paging = imConversationMembersService.getImConversationMembersPageList(imConversationMembersPageParam);
return ApiResult.ok(paging);
}
//
// /**
// * 修改会话成员表
// */
// @PostMapping("/update")
// @OperationLog(name = "修改会话成员表", type = OperationLogType.UPDATE)
// @ApiOperation(value = "修改会话成员表")
// public ApiResult<Boolean> updateImConversationMembers(@Validated(Update.class) @RequestBody ImConversationMembers imConversationMembers) throws Exception {
// boolean flag = imConversationMembersService.updateImConversationMembers(imConversationMembers);
// return ApiResult.result(flag);
// }
//
// /**
// * 删除会话成员表
// */
// @PostMapping("/delete/{id}")
// @OperationLog(name = "删除会话成员表", type = OperationLogType.DELETE)
// @ApiOperation(value = "删除会话成员表")
// public ApiResult<Boolean> deleteImConversationMembers(@PathVariable("id") Long id) throws Exception {
// boolean flag = imConversationMembersService.deleteImConversationMembers(id);
// return ApiResult.result(flag);
// }
//
// /**
// * 获取会话成员表详情
// */
// @GetMapping("/info/{id}")
// @OperationLog(name = "会话成员表详情", type = OperationLogType.INFO)
// @ApiOperation(value = "会话成员表详情")
// public ApiResult<ImConversationMembersQueryVo> getImConversationMembers(@PathVariable("id") Long id) throws Exception {
// ImConversationMembersQueryVo imConversationMembersQueryVo = imConversationMembersService.getImConversationMembersById(id);
// return ApiResult.ok(imConversationMembersQueryVo);
// }
//
// /**
// * 会话成员表分页列表
// */
// @PostMapping("/getPageList")
// @OperationLog(name = "会话成员表分页列表", type = OperationLogType.PAGE)
// @ApiOperation(value = "会话成员表分页列表")
// public ApiResult<Paging<ImConversationMembersQueryVo>> getImConversationMembersPageList(@Validated @RequestBody ImConversationMembersPageParam imConversationMembersPageParam) throws Exception {
// Paging<ImConversationMembersQueryVo> paging = imConversationMembersService.getImConversationMembersPageList(imConversationMembersPageParam);
// return ApiResult.ok(paging);
// }
}
......@@ -11,16 +11,17 @@ import com.wecloud.im.service.ImClientLoginService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.tillo.app_ws.utils.RedisUtils;
import com.wecloud.im.vo.TokenVo;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.shiro.cache.AppLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
@Service
@Slf4j
public class ImClientLoginServiceImpl implements ImClientLoginService {
......@@ -37,6 +38,15 @@ public class ImClientLoginServiceImpl implements ImClientLoginService {
@Autowired
private ImClientService imClientService;
private static JwtProperties jwtProperties;
@Autowired
private AppLoginRedisService appLoginRedisService;
public ImClientLoginServiceImpl(JwtProperties jwtProperties) {
ImClientLoginServiceImpl.jwtProperties = jwtProperties;
}
/**
* 根据客户方生成签名字符串 验证通过则下发token
*
......@@ -57,7 +67,8 @@ public class ImClientLoginServiceImpl implements ImClientLoginService {
}
// 生成以数据库为准的签名
String mySign = new MD5().digestHex(imTokenVerify.getTimestemp() + imTokenVerify.getClientId() + imApplication.getAppKey() + imApplication.getAppSecret());
String secret = imApplication.getAppSecret();
String mySign = new MD5().digestHex(imTokenVerify.getTimestemp() + imTokenVerify.getClientId() + imApplication.getAppKey() + secret);
// 验证签名
if (mySign.equals(imTokenVerify.getSign())) {
......@@ -65,31 +76,35 @@ public class ImClientLoginServiceImpl implements ImClientLoginService {
} else {
log.debug("sign不一致" + mySign);
return ApiResult.result(ApiCode.FAIL, null);
}
ImClient imClient;
// 判断client是否存在
ImClient client = imClientService.getOne(new QueryWrapper<ImClient>().lambda()
imClient = imClientService.getOne(new QueryWrapper<ImClient>().lambda()
.eq(ImClient::getFkAppid, imApplication.getId())
.eq(ImClient::getClientId, imTokenVerify.getClientId()));
if (client == null) {
if (imClient == null) {
log.debug("client不存在,先走注册流程");
ImClient imClient = new ImClient();
imClient.setId(new Snowflake(1L, 1L).nextId());
imClient.setFkAppid(imApplication.getId());
imClient.setClientId(imTokenVerify.getClientId());
imClientService.save(imClient);
}
// 生成token
String generateToken = JwtUtil.generateToken(imTokenVerify.getClientId(), imTokenVerify.getAppKey(), imApplication.getAppSecret(), Duration.ofDays(100));
String generateToken = JwtUtil.generateToken(imTokenVerify.getClientId(), imTokenVerify.getAppKey(), secret, null);
// 保存redis
redisTemplate.opsForValue().set("client:" + imApplication.getAppKey() + ":" + imTokenVerify.getClientId(), generateToken);
JwtToken jwtToken = new JwtToken()
.build(generateToken, secret, jwtProperties.getExpireSecond(), imClient.getClientId(), imTokenVerify.getAppKey());
appLoginRedisService.cacheLoginInfo(jwtToken);
TokenVo tokenVo = new TokenVo();
tokenVo.setToken(generateToken);
return ApiResult.ok(tokenVo);
......
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.config.constant;
/**
* 可排序查询参数对象
*
* @author geekidea
* @since 2019-08-08
*/
public interface CacheKey {
}
......@@ -24,26 +24,24 @@ package io.geekidea.springbootplus.config.constant;
* @author geekidea
* @date 2019-05-23
**/
public interface AppLoginRedisKey {
public interface ClientLoginRedisKey {
/**
* 登录用户信息key
* login:user:username
* login:user:client
*/
String LOGIN_USER = "app:login:user:%s";
String LOGIN_USER = "login:user:%s";
/**
* 登录用户username token
* login:user:token:username:token
* login:user:token:client:token
*/
String LOGIN_USER_TOKEN = "app:login:user:token:%s:%s";
String LOGIN_USER_TOKEN = "login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
* 登录用户下的所有token, 查询使用
* login:user:token:client:*
*/
String LOGIN_USER_ALL_TOKEN = "app:login:user:token:%s:*";
String LOGIN_USER_ALL_TOKEN = "login:user:token:%s:*";
}
......@@ -60,10 +60,6 @@ public interface CommonConstant {
*/
String PAGE_SIZE_NAME = "pageSize";
/**
* 登录用户
*/
String LOGIN_SYS_USER = "loginSysUser";
/**
* 登录token
......@@ -71,9 +67,11 @@ public interface CommonConstant {
String JWT_DEFAULT_TOKEN_NAME = "token";
/**
* JWT用户名
* JWT-id
*/
String CLIENT_ID = "clientId";
String APP_KEY = "appKey";
/**
* JWT刷新新token响应状态码
......@@ -96,37 +94,7 @@ public interface CommonConstant {
*/
Long JWT_DEFAULT_EXPIRE_SECOND = 5184000L;
/**
* 默认头像
*/
String DEFAULT_HEAD_URL = "";
/**
* 管理员角色名称
*/
String ADMIN_ROLE_NAME = "管理员";
String ADMIN_LOGIN = "adminLogin";
/**
* 验证码token
*/
String VERIFY_TOKEN = "verifyToken";
/**
* 图片
*/
String IMAGE = "image";
/**
* JPEG
*/
String JPEG = "JPEG";
/**
* base64前缀
*/
String BASE64_PREFIX = "data:image/png;base64,";
/**
* ..
......@@ -138,10 +106,6 @@ public interface CommonConstant {
*/
String SPOT_SPOT_BACKSLASH = "../";
/**
* SpringBootAdmin登录信息
*/
String ADMIN_LOGIN_SESSION = "adminLoginSession";
/**
* 用户浏览器代理
......
......@@ -34,13 +34,8 @@ public interface CommonRedisKey {
/**
* 登录用户盐值信息key
* login:salt:username
* login:salt:clientId
*/
String LOGIN_SALT = "login:salt:%s";
/**
* 验证码
* verify.code:666666
*/
String VERIFY_CODE = "verify.smscode:%s";
}
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.config.constant;
/**
* <p>
* redis key 常量
* </p>
*
* @author geekidea
* @date 2019-05-23
**/
public interface MerchantLoginRedisKey {
/**
* 登录用户信息key
* login:user:username
*/
String LOGIN_USER = "mer:login:user:%s";
/**
* 登录用户username token
* login:user:token:username:token
*/
String LOGIN_USER_TOKEN = "mer:login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
*/
String LOGIN_USER_ALL_TOKEN = "mer:login:user:token:%s:*";
}
......@@ -942,7 +942,7 @@ public abstract class BaseLogAop {
sysLoginLog.setUsername(username);
}
} else if (LOGOUT_TYPE == type) {
String username = JwtUtil.getUsername(requestInfo.getToken());
String username = JwtUtil.getClientId(requestInfo.getToken());
sysLoginLog.setUsername(username);
// 设置登出token
sysLoginLog.setToken(requestInfo.getTokenMd5());
......
......@@ -34,9 +34,8 @@ public interface AppLoginRedisService {
* 缓存登录信息
*
* @param jwtToken
* @param loginSysUserVo
*/
void cacheLoginInfo(JwtToken jwtToken, LoginUserVo loginSysUserVo);
void cacheLoginInfo(JwtToken jwtToken);
/**
......@@ -64,13 +63,13 @@ public interface AppLoginRedisService {
*/
LoginUserVo getLoginSysUserVo(String username);
// /**
// * 通过用户名称获取盐值
// *
// * @param username
// * @return
// */
// String getSalt(String username);
/**
* 通过用户名称获取盐值
*
* @param username
* @return
*/
String getSalt(String username);
/**
* 删除对应用户的Redis缓存
......
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.framework.shiro.cache;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserVo;
/**
* 登录信息Redis缓存操作服务
*
* @author geekidea
* @date 2019-09-30
* @since 1.3.0.RELEASE
**/
public interface MerchantLoginRedisService {
/**
* 缓存登录信息
*
* @param jwtToken
* @param loginSysUserVo
*/
void cacheLoginInfo(JwtToken jwtToken, LoginUserVo loginSysUserVo);
/**
* 刷新登录信息
*
* @param oldToken
* @param username
* @param newJwtToken
*/
void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken);
/**
* 通过用户名,从缓存中获取登录用户LoginSysUserRedisVo
*
* @param username
* @return
*/
LoginUserRedisVo getLoginMerUserRedisVo(String username);
/**
* 获取登录用户对象
*
* @param username
* @return
*/
LoginUserVo getLoginSysUserVo(String username);
/**
* 通过用户名称获取盐值
*
* @param username
* @return
*/
String getSalt(String username);
/**
* 删除对应用户的Redis缓存
*
* @param token
* @param username
*/
void deleteLoginInfo(String token, String username);
/**
* 判断token在redis中是否存在
*
* @param token
* @return
*/
boolean exists(String token);
/**
* 删除用户所有登录缓存
*
* @param username
*/
void deleteUserAllCache(String username);
}
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.framework.shiro.cache;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserVo;
/**
* 登录信息Redis缓存操作服务
*
* @author geekidea
* @date 2019-09-30
* @since 1.3.0.RELEASE
**/
public interface SysLoginRedisService {
/**
* 缓存登录信息
*
* @param jwtToken
* @param loginSysUserVo
*/
void cacheLoginInfo(JwtToken jwtToken, LoginUserVo loginSysUserVo);
/**
* 刷新登录信息
*
* @param oldToken
* @param username
* @param newJwtToken
*/
void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken);
/**
* 通过用户名,从缓存中获取登录用户LoginSysUserRedisVo
*
* @param username
* @return
*/
LoginUserRedisVo getLoginSysUserRedisVo(String username);
/**
* 获取登录用户对象
*
* @param username
* @return
*/
LoginUserVo getLoginSysUserVo(String username);
/**
* 通过用户名称获取盐值
*
* @param username
* @return
*/
String getSalt(String username);
/**
* 删除对应用户的Redis缓存
*
* @param token
* @param username
*/
void deleteLoginInfo(String token, String username);
/**
* 判断token在redis中是否存在
*
* @param token
* @return
*/
boolean exists(String token);
/**
* 删除用户所有登录缓存
*
* @param username
*/
void deleteUserAllCache(String username);
}
///*
// * Copyright 2019-2029 geekidea(https://github.com/geekidea)
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package io.geekidea.springbootplus.framework.shiro.cache;
//
//
//import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
//import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
//import io.geekidea.springbootplus.framework.shiro.vo.LoginUserVo;
//
///**
// * 登录信息Redis缓存操作服务
// *
// * @author geekidea
// * @date 2019-09-30
// * @since 1.3.0.RELEASE
// **/
//public interface SysLoginRedisService {
//
// /**
// * 缓存登录信息
// *
// * @param jwtToken
// * @param loginSysUserVo
// */
// void cacheLoginInfo(JwtToken jwtToken, LoginUserVo loginSysUserVo);
//
//
// /**
// * 刷新登录信息
// *
// * @param oldToken
// * @param username
// * @param newJwtToken
// */
// void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken);
//
// /**
// * 通过用户名,从缓存中获取登录用户LoginSysUserRedisVo
// *
// * @param username
// * @return
// */
// LoginUserRedisVo getLoginSysUserRedisVo(String username);
//
// /**
// * 获取登录用户对象
// *
// * @param username
// * @return
// */
// LoginUserVo getLoginSysUserVo(String username);
//
// /**
// * 通过用户名称获取盐值
// *
// * @param username
// * @return
// */
// String getSalt(String username);
//
// /**
// * 删除对应用户的Redis缓存
// *
// * @param token
// * @param username
// */
// void deleteLoginInfo(String token, String username);
//
// /**
// * 判断token在redis中是否存在
// *
// * @param token
// * @return
// */
// boolean exists(String token);
//
// /**
// * 删除用户所有登录缓存
// *
// * @param username
// */
// void deleteUserAllCache(String username);
//
//}
package io.geekidea.springbootplus.framework.shiro.cache.impl;
import io.geekidea.springbootplus.config.constant.AppLoginRedisKey;
import io.geekidea.springbootplus.config.constant.ClientLoginRedisKey;
import io.geekidea.springbootplus.config.constant.CommonRedisKey;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.framework.common.bean.ClientInfo;
import io.geekidea.springbootplus.framework.shiro.cache.AppLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.convert.LoginSysUserVoConvert;
import io.geekidea.springbootplus.framework.shiro.convert.ShiroMapstructConvert;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.JwtTokenRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserVo;
import io.geekidea.springbootplus.framework.util.ClientInfoUtil;
import io.geekidea.springbootplus.framework.util.HttpServletRequestUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
......@@ -42,35 +36,34 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
* username:num
*/
@Override
public void cacheLoginInfo(JwtToken jwtToken, LoginUserVo loginSysUserVo) {
public void cacheLoginInfo(JwtToken jwtToken) {
if (jwtToken == null) {
throw new IllegalArgumentException("jwtToken不能为空");
}
if (loginSysUserVo == null) {
throw new IllegalArgumentException("loginSysUserVo不能为空");
}
// 客户端标识
String client = jwtToken.getAppKey() + jwtToken.getClientId();
// token
String token = jwtToken.getToken();
// 盐值
String salt = jwtToken.getSalt();
// 登录用户名称
String username = loginSysUserVo.getUsername();
// token md5值
String tokenMd5 = DigestUtils.md5Hex(token);
// Redis缓存JWT Token信息
JwtTokenRedisVo jwtTokenRedisVo = ShiroMapstructConvert.INSTANCE.jwtTokenToJwtTokenRedisVo(jwtToken);
jwtTokenRedisVo.setUserId(loginSysUserVo.getId());
// JwtTokenRedisVo jwtTokenRedisVo = ShiroMapstructConvert.INSTANCE.jwtTokenToJwtTokenRedisVo(jwtToken);
// jwtTokenRedisVo.setUserId(loginSysUserVo.getId());
// 用户客户端信息
ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getRequest());
// ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getRequest());
// Redis缓存登录用户信息
// 将LoginSysUserVo对象复制到LoginSysUserRedisVo,使用mapstruct进行对象属性复制
LoginUserRedisVo loginSysUserRedisVo = LoginSysUserVoConvert.INSTANCE.voToRedisVo(loginSysUserVo);
loginSysUserRedisVo.setSalt(salt);
loginSysUserRedisVo.setClientInfo(clientInfo);
// LoginUserRedisVo loginSysUserRedisVo = LoginSysUserVoConvert.INSTANCE.voToRedisVo(loginSysUserVo);
// loginSysUserRedisVo.setSalt(salt);
// loginSysUserRedisVo.setClientInfo(clientInfo);
// Redis过期时间与JwtToken过期时间一致
Duration expireDuration = Duration.ofSeconds(jwtToken.getExpireSecond());
......@@ -78,18 +71,18 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
// 判断是否启用单个用户登录,如果是,这每个用户只有一个有效token
boolean singleLogin = jwtProperties.isSingleLogin();
if (singleLogin) {
deleteUserAllCache(username);
deleteUserAllCache(client);
}
// 1. tokenMd5:jwtTokenRedisVo
String loginTokenRedisKey = String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5);
redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration);
redisTemplate.opsForValue().set(loginTokenRedisKey, jwtToken, expireDuration);
// 2. username:loginSysUserRedisVo
redisTemplate.opsForValue().set(String.format(AppLoginRedisKey.LOGIN_USER, username), loginSysUserRedisVo, expireDuration);
redisTemplate.opsForValue().set(String.format(ClientLoginRedisKey.LOGIN_USER, client), jwtToken, expireDuration);
// 3. salt hash,方便获取盐值鉴权
redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_SALT, username), salt, expireDuration);
redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_SALT, client), salt, expireDuration);
// 4. login user token
redisTemplate.opsForValue().set(String.format(AppLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration);
redisTemplate.opsForValue().set(String.format(ClientLoginRedisKey.LOGIN_USER_TOKEN, client, tokenMd5), loginTokenRedisKey, expireDuration);
}
@Override
......@@ -99,7 +92,7 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
// 删除之前的token信息
deleteLoginInfo(oldToken, username);
// 缓存登录信息
cacheLoginInfo(newJwtToken, loginSysUserRedisVo);
cacheLoginInfo(newJwtToken);
}
@Override
......@@ -107,7 +100,7 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
return (LoginUserRedisVo) redisTemplate.opsForValue().get(String.format(AppLoginRedisKey.LOGIN_USER, username));
return (LoginUserRedisVo) redisTemplate.opsForValue().get(String.format(ClientLoginRedisKey.LOGIN_USER, username));
}
@Override
......@@ -118,15 +111,15 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
LoginUserRedisVo userRedisVo = getLoginSysUserRedisVo(username);
return userRedisVo;
}
//
// @Override
// public String getSalt(String username) {
// if (StringUtils.isBlank(username)) {
// throw new IllegalArgumentException("username不能为空");
// }
// String salt = (String) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_SALT, username));
// return salt;
// }
@Override
public String getSalt(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
String salt = (String) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_SALT, username));
return salt;
}
@Override
public void deleteLoginInfo(String token, String username) {
......@@ -140,11 +133,11 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
// 1. delete tokenMd5
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
// 2. delete username
redisTemplate.delete(String.format(AppLoginRedisKey.LOGIN_USER, username));
redisTemplate.delete(String.format(ClientLoginRedisKey.LOGIN_USER, username));
// 3. delete salt
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
// 4. delete user token
redisTemplate.delete(String.format(AppLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5));
redisTemplate.delete(String.format(ClientLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5));
}
@Override
......@@ -158,8 +151,8 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
}
@Override
public void deleteUserAllCache(String username) {
Set<String> userTokenMd5Set = redisTemplate.keys(String.format(AppLoginRedisKey.LOGIN_USER_ALL_TOKEN, username));
public void deleteUserAllCache(String client) {
Set<String> userTokenMd5Set = redisTemplate.keys(String.format(ClientLoginRedisKey.LOGIN_USER_ALL_TOKEN, client));
if (CollectionUtils.isEmpty(userTokenMd5Set)) {
return;
}
......@@ -170,9 +163,9 @@ public class AppLoginRedisServiceImpl implements AppLoginRedisService {
// 2. 删除登录用户的所有user:token信息
redisTemplate.delete(userTokenMd5Set);
// 3. 删除登录用户信息
redisTemplate.delete(String.format(AppLoginRedisKey.LOGIN_USER, username));
redisTemplate.delete(String.format(ClientLoginRedisKey.LOGIN_USER, client));
// 4. 删除登录用户盐值信息
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, client));
}
......
package io.geekidea.springbootplus.framework.shiro.cache.impl;
import io.geekidea.springbootplus.config.constant.CommonRedisKey;
import io.geekidea.springbootplus.config.constant.MerchantLoginRedisKey;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.framework.common.bean.ClientInfo;
import io.geekidea.springbootplus.framework.shiro.cache.MerchantLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.convert.LoginSysUserVoConvert;
import io.geekidea.springbootplus.framework.shiro.convert.ShiroMapstructConvert;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.JwtTokenRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserVo;
import io.geekidea.springbootplus.framework.util.ClientInfoUtil;
import io.geekidea.springbootplus.framework.util.HttpServletRequestUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.List;
import java.util.Set;
@Service
public class MerchantLoginRedisServiceImpl implements MerchantLoginRedisService {
@Autowired
private JwtProperties jwtProperties;
@Autowired
private RedisTemplate redisTemplate;
/**
* key-value: 有过期时间-->token过期时间
* 1. tokenMd5:jwtTokenRedisVo
* 2. username:loginSysUserRedisVo
* 3. username:salt
* hash: 没有过期时间,统计在线的用户信息
* username:num
*/
@Override
public void cacheLoginInfo(JwtToken jwtToken, LoginUserVo loginUserVo) {
if (jwtToken == null) {
throw new IllegalArgumentException("jwtToken不能为空");
}
if (loginUserVo == null) {
throw new IllegalArgumentException("loginSysUserVo不能为空");
}
// token
String token = jwtToken.getToken();
// 盐值
String salt = jwtToken.getSalt();
// 登录用户名称
String username = loginUserVo.getUsername();
// token md5值
String tokenMd5 = DigestUtils.md5Hex(token);
// Redis缓存JWT Token信息
JwtTokenRedisVo jwtTokenRedisVo = ShiroMapstructConvert.INSTANCE.jwtTokenToJwtTokenRedisVo(jwtToken);
jwtTokenRedisVo.setUserId(loginUserVo.getId());
jwtTokenRedisVo.setMcId(jwtToken.getMcId());
// 用户客户端信息
ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getRequest());
// Redis缓存登录用户信息
// 将LoginSysUserVo对象复制到LoginSysUserRedisVo,使用mapstruct进行对象属性复制
LoginUserRedisVo loginSysUserRedisVo = LoginSysUserVoConvert.INSTANCE.voToRedisVo(loginUserVo);
loginSysUserRedisVo.setSalt(salt);
loginSysUserRedisVo.setClientInfo(clientInfo);
// Redis过期时间与JwtToken过期时间一致
Duration expireDuration = Duration.ofSeconds(jwtToken.getExpireSecond());
// 判断是否启用单个用户登录,如果是,这每个用户只有一个有效token
boolean singleLogin = jwtProperties.isSingleLogin();
if (singleLogin) {
deleteUserAllCache(username);
}
// 1. tokenMd5:jwtTokenRedisVo
String loginTokenRedisKey = String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5);
redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration);
// 2. username:loginSysUserRedisVo
redisTemplate.opsForValue().set(String.format(MerchantLoginRedisKey.LOGIN_USER, username), loginSysUserRedisVo, expireDuration);
// 3. salt hash,方便获取盐值鉴权
redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_SALT, username), salt, expireDuration);
// 4. login user token
redisTemplate.opsForValue().set(String.format(MerchantLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration);
}
@Override
public void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken) {
// 获取缓存的登录用户信息
LoginUserRedisVo loginSysUserRedisVo = getLoginMerUserRedisVo(username);
// 删除之前的token信息
deleteLoginInfo(oldToken, username);
// 缓存登录信息
cacheLoginInfo(newJwtToken, loginSysUserRedisVo);
}
@Override
public LoginUserRedisVo getLoginMerUserRedisVo(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
return (LoginUserRedisVo) redisTemplate.opsForValue().get(String.format(MerchantLoginRedisKey.LOGIN_USER, username));
}
@Override
public LoginUserVo getLoginSysUserVo(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
LoginUserRedisVo userRedisVo = getLoginMerUserRedisVo(username);
return userRedisVo;
}
@Override
public String getSalt(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
String salt = (String) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_SALT, username));
return salt;
}
@Override
public void deleteLoginInfo(String token, String username) {
if (token == null) {
throw new IllegalArgumentException("token不能为空");
}
if (username == null) {
throw new IllegalArgumentException("username不能为空");
}
String tokenMd5 = DigestUtils.md5Hex(token);
// 1. delete tokenMd5
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
// 2. delete username
redisTemplate.delete(String.format(MerchantLoginRedisKey.LOGIN_USER, username));
// 3. delete salt
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
// 4. delete user token
redisTemplate.delete(String.format(MerchantLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5));
}
@Override
public boolean exists(String token) {
if (token == null) {
throw new IllegalArgumentException("token不能为空");
}
String tokenMd5 = DigestUtils.md5Hex(token);
Object object = redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
return object != null;
}
@Override
public void deleteUserAllCache(String username) {
Set<String> userTokenMd5Set = redisTemplate.keys(String.format(MerchantLoginRedisKey.LOGIN_USER_ALL_TOKEN, username));
if (CollectionUtils.isEmpty(userTokenMd5Set)) {
return;
}
// 1. 删除登录用户的所有token信息
List<String> tokenMd5List = redisTemplate.opsForValue().multiGet(userTokenMd5Set);
redisTemplate.delete(tokenMd5List);
// 2. 删除登录用户的所有user:token信息
redisTemplate.delete(userTokenMd5Set);
// 3. 删除登录用户信息
redisTemplate.delete(String.format(MerchantLoginRedisKey.LOGIN_USER, username));
// 4. 删除登录用户盐值信息
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
}
}
......@@ -16,13 +16,11 @@
package io.geekidea.springbootplus.framework.shiro.jwt;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
import io.geekidea.springbootplus.framework.shiro.vo.JwtTokenRedisVo;
import io.geekidea.springbootplus.framework.util.HttpServletResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
......@@ -48,12 +46,9 @@ import javax.servlet.http.HttpServletResponse;
public class JwtFilter extends AuthenticatingFilter {
private final ShiroLoginService shiroLoginService;
private final JwtProperties jwtProperties;
public JwtFilter(ShiroLoginService shiroLoginService, JwtProperties jwtProperties) {
public JwtFilter(ShiroLoginService shiroLoginService) {
this.shiroLoginService = shiroLoginService;
this.jwtProperties = jwtProperties;
}
/**
......@@ -66,7 +61,7 @@ public class JwtFilter extends AuthenticatingFilter {
* @throws Exception
*/
@Override
protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
// 从http请求头中取得token
String token = JwtTokenUtil.getToken();
if (StringUtils.isBlank(token)) {
......@@ -76,11 +71,10 @@ public class JwtFilter extends AuthenticatingFilter {
throw new AuthenticationException("JWT Token已过期,token:" + token);
}
JwtTokenRedisVo jwt = shiroLoginService.getTokenInfoForRedis(token);
String username = JwtUtil.getUsername(token);
// 从redis 获取jwt数据
JwtToken jwtToken = shiroLoginService.getTJwtTokenForRedis(token);
return JwtToken.build(token, username, jwt.getUserId(), shiroLoginService.getSalt(token), jwtProperties.getExpireSecond(), jwt.getType(),jwt.getMcId());
return JwtToken.build(token, jwtToken.getSalt(), jwtToken.getExpireSecond(), jwtToken.getClientId(), jwtToken.getAppKey());
}
......
......@@ -38,16 +38,6 @@ public class JwtToken implements HostAuthenticationToken {
private static final long serialVersionUID = 5101247566043093405L;
/**
* 客户端类型
*/
private String type;
/**
* 商家mcId
*/
private Long mcId;
/**
* 登录ip
*/
private String host;
......@@ -55,12 +45,13 @@ public class JwtToken implements HostAuthenticationToken {
/**
* 登录用户ID
*/
private Long userId;
private String clientId;
/**
* 登录用户名称
* app
*/
private String username;
private String appKey;
/**
* 登录盐值
*/
......@@ -86,22 +77,20 @@ public class JwtToken implements HostAuthenticationToken {
private String credentials;
public static JwtToken build(String token, String username,Long userId, String salt, long expireSecond, String type,Long mcId) {
public static JwtToken build(String token, String salt, long expireSecond, String clientId, String appKey) {
DecodedJWT decodedJwt = JwtUtil.getJwtInfo(token);
Date createDate = decodedJwt.getIssuedAt();
Date expireDate = decodedJwt.getExpiresAt();
return new JwtToken()
.setUsername(username)
.setUserId(userId)
.setToken(token)
.setHost(IpUtil.getRequestIp())
.setSalt(salt)
.setType(type)
.setCreateDate(createDate)
.setExpireSecond(expireSecond)
.setExpireDate(expireDate)
.setMcId(mcId);
JwtToken jwtToken = new JwtToken();
jwtToken.setClientId(clientId);
jwtToken.setAppKey(appKey);
jwtToken.setToken(token);
jwtToken.setHost(IpUtil.getRequestIp());
jwtToken.setSalt(salt);
jwtToken.setCreateDate(createDate);
jwtToken.setExpireSecond(expireSecond);
jwtToken.setExpireDate(expireDate);
return jwtToken;
}
@Override
......
......@@ -40,7 +40,7 @@ import org.apache.shiro.subject.PrincipalCollection;
@Slf4j
public class JwtRealmAppUser extends AuthorizingRealm {
private AppLoginRedisService appLoginRedisService;
private final AppLoginRedisService appLoginRedisService;
public JwtRealmAppUser(AppLoginRedisService appLoginRedisService) {
this.appLoginRedisService = appLoginRedisService;
......@@ -63,9 +63,9 @@ public class JwtRealmAppUser extends AuthorizingRealm {
// 设置角色/权限信息
JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
if (!jwtToken.getType().equals(LoginClientTypeEnum.APP.getType())) {
return null;
}
// if (!jwtToken.getType().equals(LoginClientTypeEnum.APP.getType())) {
// return null;
// }
/*
// 获取username
String username = jwtToken.getUsername();
......@@ -79,7 +79,7 @@ public class JwtRealmAppUser extends AuthorizingRealm {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色
authorizationInfo.setRoles(SetUtils.hashSet("app:all"));
authorizationInfo.setRoles(SetUtils.hashSet("client:all"));
return authorizationInfo;
}
......
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.framework.shiro.jwt.realm;
import io.geekidea.springbootplus.framework.shiro.cache.MerchantLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* Shiro 授权认证-商家
*
* @author geekidea
* @date 2019-09-27
* @since 1.3.0.RELEASE
**/
@Slf4j
public class JwtRealmMerchant extends AuthorizingRealm {
private MerchantLoginRedisService merchantLoginRedisService;
public JwtRealmMerchant(MerchantLoginRedisService merchantLoginRedisService) {
this.merchantLoginRedisService = merchantLoginRedisService;
}
@Override
public boolean supports(AuthenticationToken token) {
return token != null && token instanceof JwtToken;
}
/**
* 授权认证,设置角色/权限信息
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.debug("doGetAuthorizationInfo principalCollection...");
// 设置角色/权限信息
JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
if (!jwtToken.getType().equals(LoginClientTypeEnum.MERCHANT.getType())) {
return null;
}
// 获取username
String username = jwtToken.getUsername();
// 获取登录用户角色权限信息
LoginUserRedisVo loginSysUserRedisVo = merchantLoginRedisService.getLoginMerUserRedisVo(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色
authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode()));
// 设置权限
authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes());
return authorizationInfo;
}
/**
* 登录认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.debug("doGetAuthenticationInfo authenticationToken...");
// 校验token
JwtToken jwtToken = (JwtToken) authenticationToken;
if (jwtToken == null) {
throw new AuthenticationException("jwtToken不能为空");
}
String salt = jwtToken.getSalt();
if (StringUtils.isBlank(salt)) {
throw new AuthenticationException("salt不能为空");
}
return new SimpleAuthenticationInfo(
jwtToken,
salt,
getName()
);
}
}
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.framework.shiro.jwt.realm;
import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserRedisVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* Shiro 授权认证
*
* @author geekidea
* @date 2019-09-27
* @since 1.3.0.RELEASE
**/
@Slf4j
public class JwtRealmSystem extends AuthorizingRealm {
private SysLoginRedisService sysLoginRedisService;
public JwtRealmSystem(SysLoginRedisService sysLoginRedisService) {
this.sysLoginRedisService = sysLoginRedisService;
}
@Override
public boolean supports(AuthenticationToken token) {
return token != null && token instanceof JwtToken;
}
/**
* 获取用户权限信息,包括角色以及权限。只有当触发检测用户权限时才会调用此方法,例如checkRole,checkPermission
*
* @return AuthorizationInfo 权限信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.debug("doGetAuthorizationInfo principalCollection...");
// 设置角色/权限信息
JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
if (!jwtToken.getType().equals(LoginClientTypeEnum.SYSTEM.getType())) {
return null;
}
// 获取username
String username = jwtToken.getUsername();
// 获取登录用户角色权限信息
LoginUserRedisVo loginSysUserRedisVo = sysLoginRedisService.getLoginSysUserRedisVo(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色
authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode()));
// 设置权限
authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes());
return authorizationInfo;
}
/**
* 登录认证, 校验密码等, 这里项目使用了jwt , 所以验证JWT就行
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.debug("doGetAuthenticationInfo authenticationToken...");
// 校验token
JwtToken jwtToken = (JwtToken) authenticationToken;
if (jwtToken == null) {
throw new AuthenticationException("jwtToken不能为空");
}
String salt = jwtToken.getSalt();
if (StringUtils.isBlank(salt)) {
throw new AuthenticationException("salt不能为空");
}
return new SimpleAuthenticationInfo(
jwtToken,
salt,
getName()
);
}
}
......@@ -49,8 +49,12 @@ public interface ShiroLoginService {
*/
JwtTokenRedisVo getTokenInfoForRedis(String token);
JwtToken getTJwtTokenForRedis(String token);
/**
* 获取盐
*
* @param token
* @return
*/
......
......@@ -19,7 +19,7 @@ package io.geekidea.springbootplus.framework.shiro.service.impl;
import io.geekidea.springbootplus.config.constant.CommonConstant;
import io.geekidea.springbootplus.config.constant.CommonRedisKey;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.cache.AppLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
......@@ -47,9 +47,13 @@ import java.util.Date;
@Service
public class ShiroLoginServiceImpl implements ShiroLoginService {
// @Lazy
// @Autowired
// private SysLoginRedisService sysLoginRedisService;
@Lazy
@Autowired
private SysLoginRedisService sysLoginRedisService;
private AppLoginRedisService appLoginRedisService;
@Lazy
@Autowired
......@@ -87,12 +91,12 @@ public class ShiroLoginServiceImpl implements ShiroLoginService {
// 如果token继续发往后台,则提示,此token已失效,请使用新token,不在返回新token,返回状态码:461
// 如果Redis缓存中没有,JwtToken没有过期,则说明,已经刷新token
boolean exists = sysLoginRedisService.exists(token);
boolean exists = appLoginRedisService.exists(token);
if (!exists) {
httpServletResponse.setStatus(CommonConstant.JWT_INVALID_TOKEN_CODE);
throw new AuthenticationException("token已无效,请使用已刷新的token");
}
String username = jwtToken.getUsername();
// String username = jwtToken.getUsername();
String salt = jwtToken.getSalt();
Long expireSecond = jwtProperties.getExpireSecond();
// 生成新token字符串
......@@ -124,10 +128,27 @@ public class ShiroLoginServiceImpl implements ShiroLoginService {
}
@Override
public JwtToken getTJwtTokenForRedis(String token) {
Object jwtTokenRedisVo = null;
// 如果开启redis二次校验,或者设置为单个用户token登录,则先在redis中判断token是否存在
if (jwtProperties.isRedisCheck() || jwtProperties.isSingleLogin()) {
String tokenMd5 = DigestUtils.md5Hex(token);
jwtTokenRedisVo = redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
// boolean redisExpired = sysLoginRedisService.exists(token); // 判断是否存在
if (jwtTokenRedisVo == null) {
throw new AuthenticationException("Redis Token不存在,token:" + token);
}
}
return (JwtToken) jwtTokenRedisVo;
}
@Override
public String getSalt(String token) {
String username = JwtUtil.getUsername(token);
String clientId = JwtUtil.getClientId(token);
if (jwtProperties.isSaltCheck()) {
return sysLoginRedisService.getSalt(username);
return appLoginRedisService.getSalt(clientId);
} else {
return jwtProperties.getSecret();
}
......
......@@ -54,7 +54,7 @@ public class JwtUtil {
/**
* 生成JWT Token
*
* @param clientId 用户名
* @param clientId clientId
* @param salt 盐值
* @param expireDuration 过期时间和单位
* @return token
......@@ -88,7 +88,10 @@ public class JwtUtil {
// 生成token
Algorithm algorithm = Algorithm.HMAC256(salt);
String token = JWT.create()
// CLIENT_ID
.withClaim(CommonConstant.CLIENT_ID, clientId)
// APP_KEY
.withClaim(CommonConstant.APP_KEY, appKey)
// jwt唯一id
.withJWTId(UUIDUtil.getUuid())
// 签发人
......@@ -97,8 +100,6 @@ public class JwtUtil {
.withSubject(jwtProperties.getSubject())
// 签发的目标
.withAudience(jwtProperties.getAudience())
// .withClaim("test1","ddddddd")
.withClaim("appKey", appKey)
// 签名时间
.withIssuedAt(new Date())
// token过期时间
......@@ -156,7 +157,7 @@ public class JwtUtil {
* @param token
* @return
*/
public static String getUsername(String token) {
public static String getClientId(String token) {
if (StringUtils.isBlank(token)) {
return null;
}
......
......@@ -16,7 +16,6 @@
package io.geekidea.springbootplus.framework.shiro.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
......@@ -35,23 +34,6 @@ public class JwtTokenRedisVo implements Serializable {
private static final long serialVersionUID = 1831633309466775223L;
/**
* 用户平台类型
*/
private String type;
@ApiModelProperty("设备推送token")
private String deviceToken;
/**
* 用户系统类型
*/
private String deviceType;
/**
* mcId
*/
private Long mcId;
/**
* 登录ip
*/
private String host;
......@@ -59,12 +41,12 @@ public class JwtTokenRedisVo implements Serializable {
/**
* 登录用户ID
*/
private Long userId;
private String clientId;
/**
* 登录用户名称
* app
*/
private String username;
private String appKey;
/**
* 登录盐值
*/
......
......@@ -21,7 +21,6 @@ import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Set;
/**
* <p>
......@@ -46,28 +45,8 @@ public class LoginUserVo implements Serializable {
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("性别,0:女,1:男,默认1")
private Integer gender;
@ApiModelProperty("状态,0:禁用,1:启用,2:锁定")
private Integer state;
@ApiModelProperty("部门id")
private Long departmentId;
@ApiModelProperty("部门名称")
private String departmentName;
@ApiModelProperty("角色id")
private Long roleId;
@ApiModelProperty("角色名称")
private String roleName;
@ApiModelProperty("角色编码")
private String roleCode;
@ApiModelProperty("权限编码列表")
private Set<String> permissionCodes;
}
......@@ -50,7 +50,7 @@ public class LoginUtil {
public static LoginUserRedisVo getLoginSysUserRedisVo() {
// 获取当前登录用户
String token = JwtTokenUtil.getToken();
String username = JwtUtil.getUsername(token);
String username = JwtUtil.getClientId(token);
if (StringUtils.isBlank(username)) {
return null;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment