Commit debfa697 by giaogiao

shiro的token校验+redis

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