Commit 543097ed by hewei

Merge branch 'double_realm_test' into 'master'

初步完成shiro

See merge request hewei/Jumeirah!3
parents 19573744 0edcf9b6
...@@ -69,11 +69,24 @@ api-system api-app api-merchant ...@@ -69,11 +69,24 @@ api-system api-app api-merchant
注册业务逻辑要调用common模块中的service去操作 注册业务逻辑要调用common模块中的service去操作
## swagger文档
http://127.0.0.1:8889/api/doc.html#/home
左上角可以切换到不同的模块
## 权限验证 ## 权限验证
使用shiro框架
Shiro 的核心:
Subject(主体): 用于记录当前的操作用户,Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授权,而subject是通过SecurityManager安全管理器进行认证授权
SecurityManager(安全管理器):对Subject 进行管理,他是shiro的核心SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
Authenticator(认证器):对用户身份进行认证
Authorizer(授权器):用户通过认证后,来判断时候拥有该权限
realm:获取用户权限数据
sessionManager(会话管理):shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
CacheManager(缓存管理器):将用户权限数据存储在缓存,这样可以提高性能。
authc:所有url都必须认证通过才可以访问; authc:所有url都必须认证通过才可以访问;
......
...@@ -6,6 +6,7 @@ import io.geekidea.springbootplus.framework.log.annotation.OperationLog; ...@@ -6,6 +6,7 @@ import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -31,7 +32,7 @@ public class AppHelloWorldController { ...@@ -31,7 +32,7 @@ public class AppHelloWorldController {
*/ */
@GetMapping(value = "/world") @GetMapping(value = "/world")
@OperationLog(name = "helloWorld") @OperationLog(name = "helloWorld")
@ApiOperation(value = "Hello World", response = String.class) @ApiOperation(value = "helloWorld", response = String.class)
public ApiResult<String> helloWorld() throws IOException { public ApiResult<String> helloWorld() throws IOException {
log.debug("Hello World...app"); log.debug("Hello World...app");
return ApiResult.ok("Hello World app"); return ApiResult.ok("Hello World app");
...@@ -39,16 +40,33 @@ public class AppHelloWorldController { ...@@ -39,16 +40,33 @@ public class AppHelloWorldController {
@GetMapping(value = "/needRole") @GetMapping(value = "/needRole")
@OperationLog(name = "helloWorld") @OperationLog(name = "needRole")
@ApiOperation(value = "Hello World", response = String.class) @ApiOperation(value = "needRole", response = String.class)
public ApiResult<String> needRole() throws IOException { public ApiResult<String> needRole() throws IOException {
log.debug("Hello World...app"); log.debug("Hello World...app");
return ApiResult.ok("Hello World app"); return ApiResult.ok("Hello World app");
} }
@GetMapping(value = "/needRoleAdmin")
@OperationLog(name = "needRoleAdmin")
@ApiOperation(value = "needRoleAdmin", response = String.class)
@RequiresRoles("app:admin")
public ApiResult<String> needRoleAdmin() throws IOException {
log.debug("Hello World...app");
return ApiResult.ok("Hello World needRoleAdmin");
}
@GetMapping(value = "/needRoleAll")
@OperationLog(name = "needRoleAll")
@ApiOperation(value = "needRoleAll", response = String.class)
@RequiresRoles("app:all")
public ApiResult<String> needRoleAll() throws IOException {
log.debug("Hello World...app");
return ApiResult.ok("Hello World needRoleAll");
}
@GetMapping(value = "/noRole") @GetMapping(value = "/noRole")
@OperationLog(name = "helloWorld") @OperationLog(name = "noRole")
@ApiOperation(value = "Hello World", response = String.class) @ApiOperation(value = "noRole", response = String.class)
public ApiResult<String> noRole() throws IOException { public ApiResult<String> noRole() throws IOException {
log.debug("Hello World...app"); log.debug("Hello World...app");
return ApiResult.ok("Hello World app noRole"); return ApiResult.ok("Hello World app noRole");
......
...@@ -2,6 +2,7 @@ package com.jumeirah.api.app.controller; ...@@ -2,6 +2,7 @@ package com.jumeirah.api.app.controller;
import com.jumeirah.common.entity.AppUser; import com.jumeirah.common.entity.AppUser;
import com.jumeirah.common.param.AppUserPageParam; import com.jumeirah.common.param.AppUserPageParam;
import com.jumeirah.common.param.LoginParam;
import com.jumeirah.common.param.RegisterParam; import com.jumeirah.common.param.RegisterParam;
import com.jumeirah.common.service.AppUserService; import com.jumeirah.common.service.AppUserService;
import com.jumeirah.common.vo.AppUserQueryVo; import com.jumeirah.common.vo.AppUserQueryVo;
...@@ -38,7 +39,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -38,7 +39,7 @@ import javax.servlet.http.HttpServletResponse;
@Slf4j @Slf4j
@RestController @RestController
//@Module("api-app") //@Module("api-app")
@Api(value = "用户API", tags = {"APP相关"}) @Api(value = "用户API", tags = {"APP用户相关"})
@RequestMapping("/app/user/") @RequestMapping("/app/user/")
public class AppUserController extends BaseController { public class AppUserController extends BaseController {
...@@ -106,14 +107,20 @@ public class AppUserController extends BaseController { ...@@ -106,14 +107,20 @@ public class AppUserController extends BaseController {
@ApiOperation(value = "注册", notes = "web用户注册", response = LoginSysUserTokenVo.class) @ApiOperation(value = "注册", notes = "web用户注册", response = LoginSysUserTokenVo.class)
public ApiResult<LoginSysUserTokenVo> register(@Validated @RequestBody RegisterParam registerParam, HttpServletResponse response, @RequestHeader(required = false) String language) throws Exception { public ApiResult<LoginSysUserTokenVo> register(@Validated @RequestBody RegisterParam registerParam, HttpServletResponse response, @RequestHeader(required = false) String language) throws Exception {
return appUserService.register(registerParam, language); return appUserService.register(registerParam, language);
} }
@PostMapping("/phoneLogin") @PostMapping("/login")
@OperationLogIgnore @OperationLogIgnore
@ApiOperation(value = "手机号登陆", notes = "手机号登陆", response = LoginSysUserTokenVo.class) @ApiOperation(value = "登录", notes = "系统用户登录", response = LoginSysUserTokenVo.class)
public ApiResult<LoginSysUserTokenVo> phoneLogin(@Validated @RequestBody RegisterParam registerParam, HttpServletResponse response, @RequestHeader(required = false) String language) throws Exception { public ApiResult<LoginSysUserTokenVo> login(@Validated @RequestBody LoginParam loginParam, HttpServletResponse response, @RequestHeader(required = false) String language) throws Exception {
return appUserService.register(registerParam, language); return appUserService.login(loginParam, language);
} }
// @PostMapping("/phoneLogin")
// @OperationLogIgnore
// @ApiOperation(value = "手机号登陆", notes = "手机号登陆", response = LoginSysUserTokenVo.class)
// public ApiResult<LoginSysUserTokenVo> phoneLogin(@Validated @RequestBody RegisterParam registerParam, HttpServletResponse response, @RequestHeader(required = false) String language) throws Exception {
// return appUserService.register(registerParam, language);
// }
} }
...@@ -20,11 +20,15 @@ import com.alibaba.fastjson.JSON; ...@@ -20,11 +20,15 @@ import com.alibaba.fastjson.JSON;
import io.geekidea.springbootplus.config.properties.JwtProperties; 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.LoginRedisService; 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.JwtRealm; 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;
...@@ -41,6 +45,7 @@ import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; ...@@ -41,6 +45,7 @@ import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO; import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator; import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
...@@ -51,6 +56,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties ...@@ -51,6 +56,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
...@@ -103,13 +109,37 @@ public class ShiroConfig { ...@@ -103,13 +109,37 @@ public class ShiroConfig {
} }
/** /**
* JWT数据源验证 * 系统的用户及权限数据
* *
* @return * @return
*/ */
@Bean @Bean
public JwtRealm jwtRealm(LoginRedisService loginRedisService) { public JwtRealmSystem jwtRealmSystem(SysLoginRedisService loginRedisService) {
JwtRealm jwtRealm = new JwtRealm(loginRedisService); JwtRealmSystem jwtRealm = new JwtRealmSystem(loginRedisService);
jwtRealm.setCachingEnabled(false);
jwtRealm.setCredentialsMatcher(credentialsMatcher());
return jwtRealm;
}
/**
* app的用户及权限数据
*/
@Bean
public JwtRealmAppUser jwtRealmAppUser(AppLoginRedisService appLoginRedisService) {
JwtRealmAppUser jwtRealm = new JwtRealmAppUser(appLoginRedisService);
jwtRealm.setCachingEnabled(false);
jwtRealm.setCredentialsMatcher(credentialsMatcher());
return jwtRealm;
}
/**
* 商家的用户及权限数据
*/
@Bean
public JwtRealmMerchant jwtRealmMerchant(MerchantLoginRedisService merchantLoginRedisService) {
JwtRealmMerchant jwtRealm = new JwtRealmMerchant(merchantLoginRedisService);
jwtRealm.setCachingEnabled(false); jwtRealm.setCachingEnabled(false);
jwtRealm.setCredentialsMatcher(credentialsMatcher()); jwtRealm.setCredentialsMatcher(credentialsMatcher());
return jwtRealm; return jwtRealm;
...@@ -118,8 +148,6 @@ public class ShiroConfig { ...@@ -118,8 +148,6 @@ public class ShiroConfig {
/** /**
* subject不存储到Session中 * subject不存储到Session中
*
* @return
*/ */
@Bean @Bean
public SessionStorageEvaluator sessionStorageEvaluator() { public SessionStorageEvaluator sessionStorageEvaluator() {
...@@ -142,14 +170,20 @@ public class ShiroConfig { ...@@ -142,14 +170,20 @@ public class ShiroConfig {
/** /**
* 安全管理器配置 * 安全管理器配置
*
* @return
*/ */
@Bean @Bean
public SecurityManager securityManager(LoginRedisService loginRedisService) { public SecurityManager securityManager(SysLoginRedisService sysLoginRedisService, AppLoginRedisService appLoginRedisService, MerchantLoginRedisService merchantLoginRedisService) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置多个realms
List<Realm> realms = new ArrayList<>();
realms.add(jwtRealmSystem(sysLoginRedisService));
realms.add(jwtRealmAppUser(appLoginRedisService));
realms.add(jwtRealmMerchant(merchantLoginRedisService));
// 数据连接器 // 数据连接器
securityManager.setRealm(jwtRealm(loginRedisService)); securityManager.setRealms(realms);
// 设置session存储方式: 不使用sessionId保存 // 设置session存储方式: 不使用sessionId保存
securityManager.setSubjectDAO(subjectDAO()); securityManager.setSubjectDAO(subjectDAO());
SecurityUtils.setSecurityManager(securityManager); SecurityUtils.setSecurityManager(securityManager);
...@@ -160,7 +194,7 @@ public class ShiroConfig { ...@@ -160,7 +194,7 @@ public class ShiroConfig {
* ShiroFilterFactoryBean过滤器配置 * ShiroFilterFactoryBean过滤器配置
* *
* @param securityManager * @param securityManager
* @param loginRedisService * @param sysLoginRedisService
* @param shiroProperties * @param shiroProperties
* @param jwtProperties * @param jwtProperties
* @return * @return
...@@ -168,15 +202,16 @@ public class ShiroConfig { ...@@ -168,15 +202,16 @@ 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,
LoginRedisService loginRedisService, SysLoginRedisService sysLoginRedisService,
ShiroProperties shiroProperties, ShiroProperties shiroProperties,
JwtProperties jwtProperties) { JwtProperties jwtProperties,
RedisTemplate redisTemplate) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器 // 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置过滤器 // 设置过滤器
Map<String, Filter> filterMap = getFilterMap(shiroLoginService, loginRedisService, jwtProperties); Map<String, Filter> filterMap = getFilterMap(shiroLoginService, sysLoginRedisService, jwtProperties,redisTemplate);
shiroFilterFactoryBean.setFilters(filterMap); shiroFilterFactoryBean.setFilters(filterMap);
// 设置过滤器顺序 // 设置过滤器顺序
Map<String, String> filterChainMap = getFilterChainDefinitionMap(shiroProperties); Map<String, String> filterChainMap = getFilterChainDefinitionMap(shiroProperties);
...@@ -191,10 +226,11 @@ public class ShiroConfig { ...@@ -191,10 +226,11 @@ public class ShiroConfig {
* @return * @return
*/ */
private Map<String, Filter> getFilterMap(ShiroLoginService shiroLoginService, private Map<String, Filter> getFilterMap(ShiroLoginService shiroLoginService,
LoginRedisService loginRedisService, SysLoginRedisService loginRedisService,
JwtProperties jwtProperties) { JwtProperties jwtProperties,
RedisTemplate redisTemplate) {
Map<String, Filter> filterMap = new LinkedHashMap<>(); Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put(JWT_FILTER_NAME, new JwtFilter(shiroLoginService, loginRedisService, jwtProperties)); filterMap.put(JWT_FILTER_NAME, new JwtFilter(shiroLoginService, loginRedisService, jwtProperties, redisTemplate));
return filterMap; return filterMap;
} }
...@@ -314,13 +350,15 @@ public class ShiroConfig { ...@@ -314,13 +350,15 @@ public class ShiroConfig {
/** /**
* 认证模式 * 认证模式
* *
* @param loginRedisService * @param sysLoginRedisService
* @return * @return
*/ */
@Bean @Bean
public Authenticator authenticator(LoginRedisService loginRedisService) { public Authenticator authenticator(SysLoginRedisService sysLoginRedisService) {
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setRealms(Arrays.asList(jwtRealm(loginRedisService))); List<Realm> realms = new ArrayList<>();
realms.add(jwtRealmSystem(sysLoginRedisService));
authenticator.setRealms(realms);
// 认证策略: 第一个成功 // 认证策略: 第一个成功
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy()); authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
return authenticator; return authenticator;
......
...@@ -72,6 +72,7 @@ import static springfox.documentation.swagger.schema.ApiModelProperties.findApiM ...@@ -72,6 +72,7 @@ import static springfox.documentation.swagger.schema.ApiModelProperties.findApiM
/** /**
* Swagger2全局配置 * Swagger2全局配置
* 分组教程 https://sns.bladex.vip/q-342.html
* *
* @author geekidea * @author geekidea
* @date 2020/3/21 * @date 2020/3/21
...@@ -128,12 +129,83 @@ public class Swagger2Config { ...@@ -128,12 +129,83 @@ public class Swagger2Config {
return Optional.fromNullable(input.declaringClass()); return Optional.fromNullable(input.declaringClass());
} }
// @Bean
// public Docket createRestApi() {
// // 获取需要扫描的包
// String[] basePackages = getBasePackages();
// ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
// .apiInfo(apiInfo("默认", "1.0"))
// .select();
// // 如果扫描的包为空,则默认扫描类上有@Api注解的类
// if (ArrayUtils.isEmpty(basePackages)) {
// apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));
// } else {
// // 扫描指定的包
// apiSelectorBuilder.apis(basePackage(basePackages));
// }
// Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
// .build()
// .enable(swaggerProperties.isEnable())
// .ignoredParameterTypes(ignoredParameterTypes)
// .globalOperationParameters(getParameters());
// return docket;
// }
@Bean
public Docket restAppApi() {
// 获取需要扫描的包
String[] basePackages = {"com.jumeirah.api.app.controller"};
ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("app")
.select();
// 如果扫描的包为空,则默认扫描类上有@Api注解的类
if (ArrayUtils.isEmpty(basePackages)) {
apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));
} else {
// 扫描指定的包
apiSelectorBuilder.apis(basePackage(basePackages));
}
Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
.build()
.enable(swaggerProperties.isEnable())
.ignoredParameterTypes(ignoredParameterTypes)
.globalOperationParameters(getParameters());
return docket;
}
@Bean @Bean
public Docket createRestApi() { public Docket restSysApi() {
// 获取需要扫描的包 // 获取需要扫描的包
String[] basePackages = getBasePackages(); String[] basePackages = {"com.jumeirah.api.system.controlle"};
ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2) ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) .apiInfo(apiInfo())
.groupName("system")
.select();
// 如果扫描的包为空,则默认扫描类上有@Api注解的类
if (ArrayUtils.isEmpty(basePackages)) {
apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));
} else {
// 扫描指定的包
apiSelectorBuilder.apis(basePackage(basePackages));
}
Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
.build()
.enable(swaggerProperties.isEnable())
.ignoredParameterTypes(ignoredParameterTypes)
.globalOperationParameters(getParameters());
return docket;
}
@Bean
public Docket restMerchantApi() {
// 获取需要扫描的包
String[] basePackages = {"com.jumeirah.api.merchant.controller"};
ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("merchant")
.select(); .select();
// 如果扫描的包为空,则默认扫描类上有@Api注解的类 // 如果扫描的包为空,则默认扫描类上有@Api注解的类
if (ArrayUtils.isEmpty(basePackages)) { if (ArrayUtils.isEmpty(basePackages)) {
...@@ -150,6 +222,7 @@ public class Swagger2Config { ...@@ -150,6 +222,7 @@ public class Swagger2Config {
return docket; return docket;
} }
/** /**
* 获取apiInfo * 获取apiInfo
* *
......
...@@ -2,6 +2,7 @@ package com.jumeirah.common.service; ...@@ -2,6 +2,7 @@ package com.jumeirah.common.service;
import com.jumeirah.common.entity.AppUser; import com.jumeirah.common.entity.AppUser;
import com.jumeirah.common.param.AppUserPageParam; import com.jumeirah.common.param.AppUserPageParam;
import com.jumeirah.common.param.LoginParam;
import com.jumeirah.common.param.RegisterParam; import com.jumeirah.common.param.RegisterParam;
import com.jumeirah.common.vo.AppUserQueryVo; import com.jumeirah.common.vo.AppUserQueryVo;
import com.jumeirah.common.vo.LoginSysUserTokenVo; import com.jumeirah.common.vo.LoginSysUserTokenVo;
...@@ -18,6 +19,8 @@ import io.geekidea.springbootplus.framework.core.pagination.Paging; ...@@ -18,6 +19,8 @@ import io.geekidea.springbootplus.framework.core.pagination.Paging;
public interface AppUserService extends BaseService<AppUser> { public interface AppUserService extends BaseService<AppUser> {
ApiResult<LoginSysUserTokenVo> register(RegisterParam registerParam, String language); ApiResult<LoginSysUserTokenVo> register(RegisterParam registerParam, String language);
ApiResult<LoginSysUserTokenVo> login(LoginParam loginParam, String language);
ApiResult<LoginSysUserTokenVo> phoneLogin(RegisterParam registerParam, String language); ApiResult<LoginSysUserTokenVo> phoneLogin(RegisterParam registerParam, String language);
......
package com.jumeirah.common.service.impl; package com.jumeirah.common.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jumeirah.common.entity.AppUser; import com.jumeirah.common.entity.AppUser;
import com.jumeirah.common.enums.StateEnum;
import com.jumeirah.common.mapper.AppUserMapper; import com.jumeirah.common.mapper.AppUserMapper;
import com.jumeirah.common.param.AppUserPageParam; import com.jumeirah.common.param.AppUserPageParam;
import com.jumeirah.common.param.LoginParam;
import com.jumeirah.common.param.RegisterParam; import com.jumeirah.common.param.RegisterParam;
import com.jumeirah.common.service.AppUserService; import com.jumeirah.common.service.AppUserService;
import com.jumeirah.common.vo.AppUserQueryVo; import com.jumeirah.common.vo.AppUserQueryVo;
import com.jumeirah.common.vo.LoginSysUserTokenVo; import com.jumeirah.common.vo.LoginSysUserTokenVo;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
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.common.service.impl.BaseServiceImpl; import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo; import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging; import io.geekidea.springbootplus.framework.core.pagination.Paging;
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.SaltUtil;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
import io.geekidea.springbootplus.framework.util.PasswordUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
/** /**
* APP用户 服务实现类 * APP用户 服务实现类
* *
...@@ -30,6 +51,20 @@ import org.springframework.transaction.annotation.Transactional; ...@@ -30,6 +51,20 @@ import org.springframework.transaction.annotation.Transactional;
public class AppUserServiceImpl extends BaseServiceImpl<AppUserMapper, AppUser> implements AppUserService { public class AppUserServiceImpl extends BaseServiceImpl<AppUserMapper, AppUser> implements AppUserService {
@Autowired @Autowired
private RedisTemplate redisTemplate;
@Lazy
@Autowired
private AppLoginRedisService appLoginRedisService;
@Lazy
@Autowired
private JwtProperties jwtProperties;
@Autowired
private SpringBootPlusProperties springBootPlusProperties;
@Autowired
private AppUserMapper appUserMapper; private AppUserMapper appUserMapper;
@Override @Override
...@@ -38,6 +73,83 @@ public class AppUserServiceImpl extends BaseServiceImpl<AppUserMapper, AppUser> ...@@ -38,6 +73,83 @@ public class AppUserServiceImpl extends BaseServiceImpl<AppUserMapper, AppUser>
} }
@Override @Override
public ApiResult<LoginSysUserTokenVo> login(LoginParam loginParam, String language) {
// 校验验证码
// checkVerifyCode(loginParam.getVerifyToken(), loginParam.getCode());
String username = loginParam.getUsername();
// 从数据库中获取登录用户信息
AppUser appUser = appUserMapper.selectOne(new QueryWrapper<AppUser>(new AppUser().setUsername(username)));
if (appUser == null) {
log.error("登录失败,loginParam:{}", loginParam);
// throw new AuthenticationException("用户名或密码错误");
return ApiResult.fail(ApiCode.PWD_OR_USERNAME_ERROR, language);
}
if (StateEnum.DISABLE.getCode().equals(appUser.getState())) {
throw new AuthenticationException("账号已禁用");
}
// 实际项目中,前端传过来的密码应先加密
// 原始密码明文:123456
// 原始密码前端加密:sha256(123456)
// 后台加密规则:sha256(sha256(123456) + salt)
String encryptPassword = PasswordUtil.encrypt(loginParam.getPassword(), appUser.getSalt());
if (!encryptPassword.equals(appUser.getPassword())) {
return ApiResult.fail(ApiCode.PWD_OR_USERNAME_ERROR, language);
}
// 将系统用户对象转换成登录用户对象
LoginSysUserVo loginSysUserVo = new LoginSysUserVo();
loginSysUserVo.setUsername(username);
// 获取数据库中保存的盐值
String newSalt = SaltUtil.getSalt(appUser.getSalt(), jwtProperties);
// 生成token字符串并返回
Long expireSecond = jwtProperties.getExpireSecond();
String token = JwtUtil.generateToken(username, newSalt, Duration.ofSeconds(expireSecond));
log.debug("token:{}", token);
// 创建AuthenticationToken
JwtToken jwtToken = JwtToken.build(token, username, newSalt, expireSecond,"app");
boolean enableShiro = springBootPlusProperties.getShiro().isEnable();
if (enableShiro) {
// 从SecurityUtils里边创建一个 subject
Subject subject = SecurityUtils.getSubject();
// 执行认证登录
subject.login(jwtToken);
} else {
log.warn("未启用Shiro");
}
// 缓存登录信息到Redis
appLoginRedisService.cacheLoginInfo(jwtToken, loginSysUserVo);
log.debug("登录成功,username:{}", username);
// 缓存登录信息到redis
String tokenSha256 = DigestUtils.sha256Hex(token);
redisTemplate.opsForValue().set(tokenSha256, loginSysUserVo, 1, TimeUnit.DAYS);
// 返回token和登录用户信息对象
LoginSysUserTokenVo loginSysUserTokenVo = new LoginSysUserTokenVo();
loginSysUserTokenVo.setToken(token);
loginSysUserTokenVo.setLoginSysUserVo(loginSysUserVo);
// 设置token响应头
// response.setHeader(JwtTokenUtil.getTokenName(), loginSysUserTokenVo.getToken());
return ApiResult.ok(loginSysUserTokenVo, language);
}
@Override
public ApiResult<LoginSysUserTokenVo> phoneLogin(RegisterParam registerParam, String language) { public ApiResult<LoginSysUserTokenVo> phoneLogin(RegisterParam registerParam, String language) {
return null; return null;
} }
......
...@@ -22,26 +22,26 @@ import com.jumeirah.common.entity.SysDepartment; ...@@ -22,26 +22,26 @@ import com.jumeirah.common.entity.SysDepartment;
import com.jumeirah.common.entity.SysRole; import com.jumeirah.common.entity.SysRole;
import com.jumeirah.common.entity.SysUser; import com.jumeirah.common.entity.SysUser;
import com.jumeirah.common.enums.StateEnum; import com.jumeirah.common.enums.StateEnum;
import com.jumeirah.common.exception.VerificationCodeException;
import com.jumeirah.common.mapper.SysUserMapper; import com.jumeirah.common.mapper.SysUserMapper;
import com.jumeirah.common.param.LoginParam; import com.jumeirah.common.param.LoginParam;
import com.jumeirah.common.service.SysDepartmentService;
import com.jumeirah.common.service.SysLoginService;
import com.jumeirah.common.service.SysRolePermissionService;
import com.jumeirah.common.service.SysRoleService;
import com.jumeirah.common.vo.LoginSysUserTokenVo; import com.jumeirah.common.vo.LoginSysUserTokenVo;
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.config.properties.SpringBootPlusProperties; import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
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.LoginRedisService; import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken; import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
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.util.SaltUtil; import io.geekidea.springbootplus.framework.shiro.util.SaltUtil;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo; import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
import io.geekidea.springbootplus.framework.util.PasswordUtil; import io.geekidea.springbootplus.framework.util.PasswordUtil;
import com.jumeirah.common.exception.VerificationCodeException;
import com.jumeirah.common.service.SysLoginService;
import com.jumeirah.common.service.SysDepartmentService;
import com.jumeirah.common.service.SysRolePermissionService;
import com.jumeirah.common.service.SysRoleService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
...@@ -61,8 +61,6 @@ import java.time.Duration; ...@@ -61,8 +61,6 @@ import java.time.Duration;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.geekidea.springbootplus.framework.common.api.ApiResult.fail;
/** /**
* <p> * <p>
* 登录服务实现类 * 登录服务实现类
...@@ -78,7 +76,7 @@ public class SysLoginServiceImpl implements SysLoginService { ...@@ -78,7 +76,7 @@ public class SysLoginServiceImpl implements SysLoginService {
@Lazy @Lazy
@Autowired @Autowired
private LoginRedisService loginRedisService; private SysLoginRedisService loginRedisService;
@Lazy @Lazy
@Autowired @Autowired
...@@ -180,7 +178,7 @@ public class SysLoginServiceImpl implements SysLoginService { ...@@ -180,7 +178,7 @@ public class SysLoginServiceImpl implements SysLoginService {
log.debug("token:{}", token); log.debug("token:{}", token);
// 创建AuthenticationToken // 创建AuthenticationToken
JwtToken jwtToken = JwtToken.build(token, username, newSalt, expireSecond); JwtToken jwtToken = JwtToken.build(token, username, newSalt, expireSecond,"sys");
boolean enableShiro = springBootPlusProperties.getShiro().isEnable(); boolean enableShiro = springBootPlusProperties.getShiro().isEnable();
if (enableShiro) { if (enableShiro) {
......
/*
* 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 AppLoginRedisKey {
/**
* 登录用户token信息key
* login:token:tokenMd5
*/
String LOGIN_TOKEN = "sys:login:token:%s";
/**
* 登录用户信息key
* login:user:username
*/
String LOGIN_USER = "app:login:user:%s";
/**
* 登录用户盐值信息key
* login:salt:username
*/
// String LOGIN_SALT = "app:login:salt:%s";
String LOGIN_SALT = "sys:login:salt:%s";
/**
* 登录用户username token
* login:user:token:username:token
*/
String LOGIN_USER_TOKEN = "app:login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
*/
String LOGIN_USER_ALL_TOKEN = "app:login:user:token:%s:*";
}
...@@ -92,9 +92,9 @@ public interface CommonConstant { ...@@ -92,9 +92,9 @@ public interface CommonConstant {
String JWT_DEFAULT_SECRET = "666666"; String JWT_DEFAULT_SECRET = "666666";
/** /**
* JWT 默认过期时间,3600L,单位秒 * JWT 默认过期时间,5184000L = 60天,单位秒
*/ */
Long JWT_DEFAULT_EXPIRE_SECOND = 3600L; Long JWT_DEFAULT_EXPIRE_SECOND = 5184000L;
/** /**
* 默认头像 * 默认头像
......
...@@ -27,38 +27,8 @@ package io.geekidea.springbootplus.config.constant; ...@@ -27,38 +27,8 @@ package io.geekidea.springbootplus.config.constant;
public interface CommonRedisKey { public interface CommonRedisKey {
/** /**
* 登录用户token信息key
* login:token:tokenMd5
*/
String LOGIN_TOKEN = "login:token:%s";
/**
* 登录用户信息key
* login:user:username
*/
String LOGIN_USER = "login:user:%s";
/**
* 登录用户盐值信息key
* login:salt:username
*/
String LOGIN_SALT = "login:salt:%s";
/**
* 登录用户username token
* login:user:token:username:token
*/
String LOGIN_USER_TOKEN = "login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
*/
String LOGIN_USER_ALL_TOKEN = "login:user:token:%s:*";
/**
* 验证码 * 验证码
* verify.code:666666 * verify.code:666666
*/ */
String VERIFY_CODE = "verify.code:%s"; 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 {
/**
* 登录用户token信息key
* login:token:tokenMd5
*/
String LOGIN_TOKEN = "sys:login:token:%s";
/**
* 登录用户信息key
* login:user:username
*/
String LOGIN_USER = "mer:login:user:%s";
/**
* 登录用户盐值信息key
* login:salt:username
*/
String LOGIN_SALT = "sys:login:salt:%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:*";
}
/*
* 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 SysLoginRedisKey {
/**
* 登录用户token信息key
* login:token:tokenMd5
*/
String LOGIN_TOKEN = "sys:login:token:%s";
/**
* 登录用户信息key
* login:user:username
*/
String LOGIN_USER = "sys:login:user:%s";
/**
* 登录用户盐值信息key
* login:salt:username
*/
String LOGIN_SALT = "sys:login:salt:%s";
/**
* 登录用户username token
* login:user:token:username:token
*/
String LOGIN_USER_TOKEN = "sys:login:user:token:%s:%s";
/**
* 登录用户下的所有token
* login:user:token:username:*
*/
String LOGIN_USER_ALL_TOKEN = "sys:login:user:token:%s:*";
}
...@@ -7,7 +7,7 @@ server: ...@@ -7,7 +7,7 @@ server:
context-path: /api context-path: /api
tomcat: tomcat:
max-threads: 200 max-threads: 200
min-spare-threads: 10 min-spare-threads: 5
uri-encoding: UTF-8 uri-encoding: UTF-8
############################# 访问路径、端口tomcat end ############################### ############################# 访问路径、端口tomcat end ###############################
...@@ -183,7 +183,7 @@ spring-boot-plus: ...@@ -183,7 +183,7 @@ spring-boot-plus:
# 权限配置 # 权限配置
anon: anon:
# 排除登录 注册 登出 # 排除登录 注册 登出
- /app/user/register,/app/user/phoneLogin,/sys/login,/sys/logout,/sys/register - /app/user/register,/app/user/phoneLogin,/app/user/login,/sys/login,/sys/logout,/sys/register
# 排除静态资源 # 排除静态资源
- /static/**,/templates/** - /static/**,/templates/**
# 排除Swagger # 排除Swagger
...@@ -200,7 +200,7 @@ spring-boot-plus: ...@@ -200,7 +200,7 @@ spring-boot-plus:
/upload/**=anon /upload/**=anon
/verificationCode/**=anon /verificationCode/**=anon
/enum=anon /enum=anon
# /app/**=authc /app/*=roles["app:all"]
######################## Spring Shiro end ########################## ######################## Spring Shiro end ##########################
......
...@@ -45,7 +45,6 @@ import io.geekidea.springbootplus.framework.util.ClientInfoUtil; ...@@ -45,7 +45,6 @@ import io.geekidea.springbootplus.framework.util.ClientInfoUtil;
import io.geekidea.springbootplus.framework.util.DateUtil; import io.geekidea.springbootplus.framework.util.DateUtil;
import io.geekidea.springbootplus.framework.util.IpUtil; import io.geekidea.springbootplus.framework.util.IpUtil;
import io.geekidea.springbootplus.framework.util.Jackson; import io.geekidea.springbootplus.framework.util.Jackson;
import io.geekidea.springbootplus.framework.util.LoginUtil;
import io.geekidea.springbootplus.framework.util.UUIDUtil; import io.geekidea.springbootplus.framework.util.UUIDUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
...@@ -824,7 +823,7 @@ public abstract class BaseLogAop { ...@@ -824,7 +823,7 @@ public abstract class BaseLogAop {
} }
// 设置当前登录信息 // 设置当前登录信息
sysOperationLog.setUserId(LoginUtil.getUserId()).setUserName(LoginUtil.getUsername()); // sysOperationLog.setUserId(LoginUtil.getUserId()).setUserName(LoginUtil.getUsername());
// 设置异常信息 // 设置异常信息
if (exception != null) { if (exception != null) {
......
...@@ -28,7 +28,7 @@ import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo; ...@@ -28,7 +28,7 @@ import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
* @date 2019-09-30 * @date 2019-09-30
* @since 1.3.0.RELEASE * @since 1.3.0.RELEASE
**/ **/
public interface LoginRedisService { public interface AppLoginRedisService {
/** /**
* 缓存登录信息 * 缓存登录信息
......
/*
* 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.LoginSysUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
/**
* 登录信息Redis缓存操作服务
*
* @author geekidea
* @date 2019-09-30
* @since 1.3.0.RELEASE
**/
public interface MerchantLoginRedisService {
/**
* 缓存登录信息
*
* @param jwtToken
* @param loginSysUserVo
*/
void cacheLoginInfo(JwtToken jwtToken, LoginSysUserVo loginSysUserVo);
/**
* 刷新登录信息
*
* @param oldToken
* @param username
* @param newJwtToken
*/
void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken);
/**
* 通过用户名,从缓存中获取登录用户LoginSysUserRedisVo
*
* @param username
* @return
*/
LoginSysUserRedisVo getLoginSysUserRedisVo(String username);
/**
* 获取登录用户对象
*
* @param username
* @return
*/
LoginSysUserVo 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.LoginSysUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
/**
* 登录信息Redis缓存操作服务
*
* @author geekidea
* @date 2019-09-30
* @since 1.3.0.RELEASE
**/
public interface SysLoginRedisService {
/**
* 缓存登录信息
*
* @param jwtToken
* @param loginSysUserVo
*/
void cacheLoginInfo(JwtToken jwtToken, LoginSysUserVo loginSysUserVo);
/**
* 刷新登录信息
*
* @param oldToken
* @param username
* @param newJwtToken
*/
void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken);
/**
* 通过用户名,从缓存中获取登录用户LoginSysUserRedisVo
*
* @param username
* @return
*/
LoginSysUserRedisVo getLoginSysUserRedisVo(String username);
/**
* 获取登录用户对象
*
* @param username
* @return
*/
LoginSysUserVo 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.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.LoginSysUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
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 AppLoginRedisServiceImpl implements AppLoginRedisService {
@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, LoginSysUserVo loginSysUserVo) {
if (jwtToken == null) {
throw new IllegalArgumentException("jwtToken不能为空");
}
if (loginSysUserVo == null) {
throw new IllegalArgumentException("loginSysUserVo不能为空");
}
// 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);
// 用户客户端信息
ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getRequest());
// Redis缓存登录用户信息
// 将LoginSysUserVo对象复制到LoginSysUserRedisVo,使用mapstruct进行对象属性复制
LoginSysUserRedisVo loginSysUserRedisVo = LoginSysUserVoConvert.INSTANCE.voToRedisVo(loginSysUserVo);
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(AppLoginRedisKey.LOGIN_TOKEN, tokenMd5);
redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration);
// 2. username:loginSysUserRedisVo
redisTemplate.opsForValue().set(String.format(AppLoginRedisKey.LOGIN_USER, username), loginSysUserRedisVo, expireDuration);
// 3. salt hash,方便获取盐值鉴权
redisTemplate.opsForValue().set(String.format(AppLoginRedisKey.LOGIN_SALT, username), salt, expireDuration);
// 4. login user token
redisTemplate.opsForValue().set(String.format(AppLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration);
}
@Override
public void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken) {
// 获取缓存的登录用户信息
LoginSysUserRedisVo loginSysUserRedisVo = getLoginSysUserRedisVo(username);
// 删除之前的token信息
deleteLoginInfo(oldToken, username);
// 缓存登录信息
cacheLoginInfo(newJwtToken, loginSysUserRedisVo);
}
@Override
public LoginSysUserRedisVo getLoginSysUserRedisVo(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(AppLoginRedisKey.LOGIN_USER, username));
}
@Override
public LoginSysUserVo getLoginSysUserVo(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
LoginSysUserRedisVo 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(AppLoginRedisKey.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(AppLoginRedisKey.LOGIN_TOKEN, tokenMd5));
// 2. delete username
redisTemplate.delete(String.format(AppLoginRedisKey.LOGIN_USER, username));
// 3. delete salt
redisTemplate.delete(String.format(AppLoginRedisKey.LOGIN_SALT, username));
// 4. delete user token
redisTemplate.delete(String.format(AppLoginRedisKey.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(AppLoginRedisKey.LOGIN_TOKEN, tokenMd5));
return object != null;
}
@Override
public void deleteUserAllCache(String username) {
Set<String> userTokenMd5Set = redisTemplate.keys(String.format(AppLoginRedisKey.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(AppLoginRedisKey.LOGIN_USER, username));
// 4. 删除登录用户盐值信息
redisTemplate.delete(String.format(AppLoginRedisKey.LOGIN_SALT, username));
}
}
package io.geekidea.springbootplus.framework.shiro.cache.impl;
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.LoginSysUserRedisVo;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
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, LoginSysUserVo loginSysUserVo) {
if (jwtToken == null) {
throw new IllegalArgumentException("jwtToken不能为空");
}
if (loginSysUserVo == null) {
throw new IllegalArgumentException("loginSysUserVo不能为空");
}
// 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);
// 用户客户端信息
ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getRequest());
// Redis缓存登录用户信息
// 将LoginSysUserVo对象复制到LoginSysUserRedisVo,使用mapstruct进行对象属性复制
LoginSysUserRedisVo loginSysUserRedisVo = LoginSysUserVoConvert.INSTANCE.voToRedisVo(loginSysUserVo);
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(MerchantLoginRedisKey.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(MerchantLoginRedisKey.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) {
// 获取缓存的登录用户信息
LoginSysUserRedisVo loginSysUserRedisVo = getLoginSysUserRedisVo(username);
// 删除之前的token信息
deleteLoginInfo(oldToken, username);
// 缓存登录信息
cacheLoginInfo(newJwtToken, loginSysUserRedisVo);
}
@Override
public LoginSysUserRedisVo getLoginSysUserRedisVo(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(MerchantLoginRedisKey.LOGIN_USER, username));
}
@Override
public LoginSysUserVo getLoginSysUserVo(String username) {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空");
}
LoginSysUserRedisVo 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(MerchantLoginRedisKey.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(MerchantLoginRedisKey.LOGIN_TOKEN, tokenMd5));
// 2. delete username
redisTemplate.delete(String.format(MerchantLoginRedisKey.LOGIN_USER, username));
// 3. delete salt
redisTemplate.delete(String.format(MerchantLoginRedisKey.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(MerchantLoginRedisKey.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(MerchantLoginRedisKey.LOGIN_SALT, username));
}
}
...@@ -16,10 +16,10 @@ ...@@ -16,10 +16,10 @@
package io.geekidea.springbootplus.framework.shiro.cache.impl; package io.geekidea.springbootplus.framework.shiro.cache.impl;
import io.geekidea.springbootplus.config.constant.CommonRedisKey; import io.geekidea.springbootplus.config.constant.SysLoginRedisKey;
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.common.bean.ClientInfo;
import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService; import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.convert.LoginSysUserVoConvert; import io.geekidea.springbootplus.framework.shiro.convert.LoginSysUserVoConvert;
import io.geekidea.springbootplus.framework.shiro.convert.ShiroMapstructConvert; import io.geekidea.springbootplus.framework.shiro.convert.ShiroMapstructConvert;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken; import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
...@@ -47,7 +47,7 @@ import java.util.Set; ...@@ -47,7 +47,7 @@ import java.util.Set;
* @since 1.3.0.RELEASE * @since 1.3.0.RELEASE
**/ **/
@Service @Service
public class LoginRedisServiceImpl implements LoginRedisService { public class SysLoginRedisServiceImpl implements SysLoginRedisService {
@Autowired @Autowired
private JwtProperties jwtProperties; private JwtProperties jwtProperties;
...@@ -102,14 +102,14 @@ public class LoginRedisServiceImpl implements LoginRedisService { ...@@ -102,14 +102,14 @@ public class LoginRedisServiceImpl implements LoginRedisService {
} }
// 1. tokenMd5:jwtTokenRedisVo // 1. tokenMd5:jwtTokenRedisVo
String loginTokenRedisKey = String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5); String loginTokenRedisKey = String.format(SysLoginRedisKey.LOGIN_TOKEN, tokenMd5);
redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration); redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration);
// 2. username:loginSysUserRedisVo // 2. username:loginSysUserRedisVo
redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_USER, username), loginSysUserRedisVo, expireDuration); redisTemplate.opsForValue().set(String.format(SysLoginRedisKey.LOGIN_USER, username), loginSysUserRedisVo, expireDuration);
// 3. salt hash,方便获取盐值鉴权 // 3. salt hash,方便获取盐值鉴权
redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_SALT, username), salt, expireDuration); redisTemplate.opsForValue().set(String.format(SysLoginRedisKey.LOGIN_SALT, username), salt, expireDuration);
// 4. login user token // 4. login user token
redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration); redisTemplate.opsForValue().set(String.format(SysLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration);
} }
@Override @Override
...@@ -127,7 +127,7 @@ public class LoginRedisServiceImpl implements LoginRedisService { ...@@ -127,7 +127,7 @@ public class LoginRedisServiceImpl implements LoginRedisService {
if (StringUtils.isBlank(username)) { if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("username不能为空"); throw new IllegalArgumentException("username不能为空");
} }
return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, username)); return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(SysLoginRedisKey.LOGIN_USER, username));
} }
@Override @Override
...@@ -144,7 +144,7 @@ public class LoginRedisServiceImpl implements LoginRedisService { ...@@ -144,7 +144,7 @@ public class LoginRedisServiceImpl implements LoginRedisService {
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(SysLoginRedisKey.LOGIN_SALT, username));
return salt; return salt;
} }
...@@ -158,13 +158,13 @@ public class LoginRedisServiceImpl implements LoginRedisService { ...@@ -158,13 +158,13 @@ public class LoginRedisServiceImpl implements LoginRedisService {
} }
String tokenMd5 = DigestUtils.md5Hex(token); String tokenMd5 = DigestUtils.md5Hex(token);
// 1. delete tokenMd5 // 1. delete tokenMd5
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5)); redisTemplate.delete(String.format(SysLoginRedisKey.LOGIN_TOKEN, tokenMd5));
// 2. delete username // 2. delete username
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER, username)); redisTemplate.delete(String.format(SysLoginRedisKey.LOGIN_USER, username));
// 3. delete salt // 3. delete salt
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username)); redisTemplate.delete(String.format(SysLoginRedisKey.LOGIN_SALT, username));
// 4. delete user token // 4. delete user token
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER_TOKEN, username, tokenMd5)); redisTemplate.delete(String.format(SysLoginRedisKey.LOGIN_USER_TOKEN, username, tokenMd5));
} }
@Override @Override
...@@ -173,13 +173,13 @@ public class LoginRedisServiceImpl implements LoginRedisService { ...@@ -173,13 +173,13 @@ public class LoginRedisServiceImpl implements LoginRedisService {
throw new IllegalArgumentException("token不能为空"); throw new IllegalArgumentException("token不能为空");
} }
String tokenMd5 = DigestUtils.md5Hex(token); String tokenMd5 = DigestUtils.md5Hex(token);
Object object = redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5)); Object object = redisTemplate.opsForValue().get(String.format(SysLoginRedisKey.LOGIN_TOKEN, tokenMd5));
return object != null; return object != null;
} }
@Override @Override
public void deleteUserAllCache(String username) { public void deleteUserAllCache(String username) {
Set<String> userTokenMd5Set = redisTemplate.keys(String.format(CommonRedisKey.LOGIN_USER_ALL_TOKEN, username)); Set<String> userTokenMd5Set = redisTemplate.keys(String.format(SysLoginRedisKey.LOGIN_USER_ALL_TOKEN, username));
if (CollectionUtils.isEmpty(userTokenMd5Set)) { if (CollectionUtils.isEmpty(userTokenMd5Set)) {
return; return;
} }
...@@ -190,9 +190,9 @@ public class LoginRedisServiceImpl implements LoginRedisService { ...@@ -190,9 +190,9 @@ public class LoginRedisServiceImpl implements LoginRedisService {
// 2. 删除登录用户的所有user:token信息 // 2. 删除登录用户的所有user:token信息
redisTemplate.delete(userTokenMd5Set); redisTemplate.delete(userTokenMd5Set);
// 3. 删除登录用户信息 // 3. 删除登录用户信息
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER, username)); redisTemplate.delete(String.format(SysLoginRedisKey.LOGIN_USER, username));
// 4. 删除登录用户盐值信息 // 4. 删除登录用户盐值信息
redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username)); redisTemplate.delete(String.format(SysLoginRedisKey.LOGIN_SALT, username));
} }
......
...@@ -16,21 +16,25 @@ ...@@ -16,21 +16,25 @@
package io.geekidea.springbootplus.framework.shiro.jwt; package io.geekidea.springbootplus.framework.shiro.jwt;
import io.geekidea.springbootplus.config.constant.SysLoginRedisKey;
import io.geekidea.springbootplus.config.properties.JwtProperties; 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.LoginRedisService; import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
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.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter; import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils; import org.apache.shiro.web.util.WebUtils;
import org.springframework.data.redis.core.RedisTemplate;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
...@@ -47,16 +51,19 @@ import javax.servlet.http.HttpServletResponse; ...@@ -47,16 +51,19 @@ import javax.servlet.http.HttpServletResponse;
@Slf4j @Slf4j
public class JwtFilter extends AuthenticatingFilter { public class JwtFilter extends AuthenticatingFilter {
private RedisTemplate redisTemplate;
private final ShiroLoginService shiroLoginService; private final ShiroLoginService shiroLoginService;
private final LoginRedisService loginRedisService; private final SysLoginRedisService sysLoginRedisService;
private final JwtProperties jwtProperties; private final JwtProperties jwtProperties;
public JwtFilter(ShiroLoginService shiroLoginService, LoginRedisService loginRedisService, JwtProperties jwtProperties) { public JwtFilter(ShiroLoginService shiroLoginService, SysLoginRedisService loginRedisService, JwtProperties jwtProperties, RedisTemplate redisTemplate) {
this.shiroLoginService = shiroLoginService; this.shiroLoginService = shiroLoginService;
this.loginRedisService = loginRedisService; this.sysLoginRedisService = loginRedisService;
this.jwtProperties = jwtProperties; this.jwtProperties = jwtProperties;
this.redisTemplate = redisTemplate;
} }
/** /**
...@@ -77,22 +84,29 @@ public class JwtFilter extends AuthenticatingFilter { ...@@ -77,22 +84,29 @@ public class JwtFilter extends AuthenticatingFilter {
throw new AuthenticationException("JWT Token已过期,token:" + token); throw new AuthenticationException("JWT Token已过期,token:" + token);
} }
Object jwtTokenRedisVo = null;
// 如果开启redis二次校验,或者设置为单个用户token登录,则先在redis中判断token是否存在 // 如果开启redis二次校验,或者设置为单个用户token登录,则先在redis中判断token是否存在
if (jwtProperties.isRedisCheck() || jwtProperties.isSingleLogin()) { if (jwtProperties.isRedisCheck() || jwtProperties.isSingleLogin()) {
boolean redisExpired = loginRedisService.exists(token); String tokenMd5 = DigestUtils.md5Hex(token);
if (!redisExpired) { jwtTokenRedisVo = redisTemplate.opsForValue().get(String.format(SysLoginRedisKey.LOGIN_TOKEN, tokenMd5));
// boolean redisExpired = sysLoginRedisService.exists(token);
if (jwtTokenRedisVo == null) {
throw new AuthenticationException("Redis Token不存在,token:" + token); throw new AuthenticationException("Redis Token不存在,token:" + token);
} }
} }
String username = JwtUtil.getUsername(token); String username = JwtUtil.getUsername(token);
String salt; String salt;
if (jwtProperties.isSaltCheck()) { if (jwtProperties.isSaltCheck()) {
salt = loginRedisService.getSalt(username); salt = sysLoginRedisService.getSalt(username);
} else { } else {
salt = jwtProperties.getSecret(); salt = jwtProperties.getSecret();
} }
return JwtToken.build(token, username, salt, jwtProperties.getExpireSecond()); JwtTokenRedisVo jwt = (JwtTokenRedisVo) jwtTokenRedisVo;
return JwtToken.build(token, username, salt, jwtProperties.getExpireSecond(), jwt.getType());
} }
/** /**
...@@ -118,6 +132,7 @@ public class JwtFilter extends AuthenticatingFilter { ...@@ -118,6 +132,7 @@ public class JwtFilter extends AuthenticatingFilter {
} }
/** /**
* 执行登陆认证
* 判断是否允许访问 * 判断是否允许访问
* *
* @param request * @param request
......
/*
* 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;
import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
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 LoginRedisService loginRedisService;
public JwtRealmSystem(LoginRedisService loginRedisService) {
this.loginRedisService = loginRedisService;
}
@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();
// 获取username
String username = jwtToken.getUsername();
// 获取登录用户角色权限信息
LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(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()
);
}
}
...@@ -37,6 +37,8 @@ import java.util.Date; ...@@ -37,6 +37,8 @@ import java.util.Date;
public class JwtToken implements HostAuthenticationToken { public class JwtToken implements HostAuthenticationToken {
private static final long serialVersionUID = 5101247566043093405L; private static final long serialVersionUID = 5101247566043093405L;
private String type;
/** /**
* 登录ip * 登录ip
*/ */
...@@ -70,7 +72,7 @@ public class JwtToken implements HostAuthenticationToken { ...@@ -70,7 +72,7 @@ public class JwtToken implements HostAuthenticationToken {
private String credentials; private String credentials;
public static JwtToken build(String token, String username, String salt, long expireSecond) { public static JwtToken build(String token, String username, String salt, long expireSecond,String type) {
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();
...@@ -79,6 +81,7 @@ public class JwtToken implements HostAuthenticationToken { ...@@ -79,6 +81,7 @@ public class JwtToken implements HostAuthenticationToken {
.setToken(token) .setToken(token)
.setHost(IpUtil.getRequestIp()) .setHost(IpUtil.getRequestIp())
.setSalt(salt) .setSalt(salt)
.setType(type)
.setCreateDate(createDate) .setCreateDate(createDate)
.setExpireSecond(expireSecond) .setExpireSecond(expireSecond)
.setExpireDate(expireDate); .setExpireDate(expireDate);
......
...@@ -16,9 +16,8 @@ ...@@ -16,9 +16,8 @@
package io.geekidea.springbootplus.framework.shiro.jwt.realm; package io.geekidea.springbootplus.framework.shiro.jwt.realm;
import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService; 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.vo.LoginSysUserRedisVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils; import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
...@@ -41,10 +40,10 @@ import org.apache.shiro.subject.PrincipalCollection; ...@@ -41,10 +40,10 @@ import org.apache.shiro.subject.PrincipalCollection;
@Slf4j @Slf4j
public class JwtRealmAppUser extends AuthorizingRealm { public class JwtRealmAppUser extends AuthorizingRealm {
private LoginRedisService loginRedisService; private AppLoginRedisService appLoginRedisService;
public JwtRealmAppUser(LoginRedisService loginRedisService) { public JwtRealmAppUser(AppLoginRedisService appLoginRedisService) {
this.loginRedisService = loginRedisService; this.appLoginRedisService = appLoginRedisService;
} }
@Override @Override
...@@ -63,15 +62,24 @@ public class JwtRealmAppUser extends AuthorizingRealm { ...@@ -63,15 +62,24 @@ public class JwtRealmAppUser extends AuthorizingRealm {
log.debug("doGetAuthorizationInfo principalCollection..."); log.debug("doGetAuthorizationInfo principalCollection...");
// 设置角色/权限信息 // 设置角色/权限信息
JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal(); JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
if (!jwtToken.getType().equals("app")) {
return null;
}
/*
// 获取username // 获取username
String username = jwtToken.getUsername(); String username = jwtToken.getUsername();
// 获取登录用户角色权限信息 // 获取登录用户角色权限信息
LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(username); LoginSysUserRedisVo loginSysUserRedisVo = appLoginRedisService.getLoginSysUserRedisVo(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色 // 设置角色
authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode())); authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode()));
// 设置权限 // 设置权限
authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes()); authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes());*/
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色
authorizationInfo.setRoles(SetUtils.hashSet("app:all"));
return authorizationInfo; return authorizationInfo;
} }
......
...@@ -14,9 +14,10 @@ ...@@ -14,9 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package io.geekidea.springbootplus.framework.shiro.jwt; package io.geekidea.springbootplus.framework.shiro.jwt.realm;
import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService; import io.geekidea.springbootplus.framework.shiro.cache.MerchantLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo; import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils; import org.apache.commons.collections4.SetUtils;
...@@ -40,10 +41,10 @@ import org.apache.shiro.subject.PrincipalCollection; ...@@ -40,10 +41,10 @@ import org.apache.shiro.subject.PrincipalCollection;
@Slf4j @Slf4j
public class JwtRealmMerchant extends AuthorizingRealm { public class JwtRealmMerchant extends AuthorizingRealm {
private LoginRedisService loginRedisService; private MerchantLoginRedisService merchantLoginRedisService;
public JwtRealmMerchant(LoginRedisService loginRedisService) { public JwtRealmMerchant(MerchantLoginRedisService merchantLoginRedisService) {
this.loginRedisService = loginRedisService; this.merchantLoginRedisService = merchantLoginRedisService;
} }
@Override @Override
...@@ -59,19 +60,27 @@ public class JwtRealmMerchant extends AuthorizingRealm { ...@@ -59,19 +60,27 @@ public class JwtRealmMerchant extends AuthorizingRealm {
*/ */
@Override @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.debug("doGetAuthorizationInfo principalCollection..."); log.debug("doGetAuthorizationInfo principalCollection...");
// 设置角色/权限信息 // 设置角色/权限信息
JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal(); JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
if (!jwtToken.getType().equals("mer")) {
return null;
}
// 获取username // 获取username
String username = jwtToken.getUsername(); String username = jwtToken.getUsername();
// 获取登录用户角色权限信息 // 获取登录用户角色权限信息
LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(username); LoginSysUserRedisVo loginSysUserRedisVo = merchantLoginRedisService.getLoginSysUserRedisVo(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色 // 设置角色
authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode())); authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode()));
// 设置权限 // 设置权限
authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes()); authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes());
return authorizationInfo; return authorizationInfo;
} }
/** /**
......
...@@ -14,9 +14,10 @@ ...@@ -14,9 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package io.geekidea.springbootplus.framework.shiro.jwt; package io.geekidea.springbootplus.framework.shiro.jwt.realm;
import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService; import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo; import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.SetUtils; import org.apache.commons.collections4.SetUtils;
...@@ -38,12 +39,12 @@ import org.apache.shiro.subject.PrincipalCollection; ...@@ -38,12 +39,12 @@ import org.apache.shiro.subject.PrincipalCollection;
* @since 1.3.0.RELEASE * @since 1.3.0.RELEASE
**/ **/
@Slf4j @Slf4j
public class JwtRealm extends AuthorizingRealm { public class JwtRealmSystem extends AuthorizingRealm {
private LoginRedisService loginRedisService; private SysLoginRedisService sysLoginRedisService;
public JwtRealm(LoginRedisService loginRedisService) { public JwtRealmSystem(SysLoginRedisService sysLoginRedisService) {
this.loginRedisService = loginRedisService; this.sysLoginRedisService = sysLoginRedisService;
} }
@Override @Override
...@@ -52,21 +53,25 @@ public class JwtRealm extends AuthorizingRealm { ...@@ -52,21 +53,25 @@ public class JwtRealm extends AuthorizingRealm {
} }
/** /**
* 授权认证,设置角色/权限信息 * 获取用户权限信息,包括角色以及权限。只有当触发检测用户权限时才会调用此方法,例如checkRole,checkPermission
* *
* @param principalCollection * @return AuthorizationInfo 权限信息
* @return
*/ */
@Override @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.debug("doGetAuthorizationInfo principalCollection..."); log.debug("doGetAuthorizationInfo principalCollection...");
// 设置角色/权限信息 // 设置角色/权限信息
JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal(); JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
if (!jwtToken.getType().equals("sys")){
return null;
}
// 获取username // 获取username
String username = jwtToken.getUsername(); String username = jwtToken.getUsername();
// 获取登录用户角色权限信息 // 获取登录用户角色权限信息
LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(username); LoginSysUserRedisVo loginSysUserRedisVo = sysLoginRedisService.getLoginSysUserRedisVo(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 设置角色 // 设置角色
authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode())); authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode()));
// 设置权限 // 设置权限
......
...@@ -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.properties.JwtProperties; import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties; import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService; import io.geekidea.springbootplus.framework.shiro.cache.SysLoginRedisService;
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.JwtTokenUtil; import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
...@@ -50,7 +50,7 @@ public class ShiroLoginServiceImpl implements ShiroLoginService { ...@@ -50,7 +50,7 @@ public class ShiroLoginServiceImpl implements ShiroLoginService {
@Lazy @Lazy
@Autowired @Autowired
private LoginRedisService loginRedisService; private SysLoginRedisService sysLoginRedisService;
@Lazy @Lazy
@Autowired @Autowired
...@@ -92,7 +92,7 @@ public class ShiroLoginServiceImpl implements ShiroLoginService { ...@@ -92,7 +92,7 @@ public class ShiroLoginServiceImpl implements ShiroLoginService {
// 如果token继续发往后台,则提示,此token已失效,请使用新token,不在返回新token,返回状态码:461 // 如果token继续发往后台,则提示,此token已失效,请使用新token,不在返回新token,返回状态码:461
// 如果Redis缓存中没有,JwtToken没有过期,则说明,已经刷新token // 如果Redis缓存中没有,JwtToken没有过期,则说明,已经刷新token
boolean exists = loginRedisService.exists(token); boolean exists = sysLoginRedisService.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");
...@@ -103,9 +103,9 @@ public class ShiroLoginServiceImpl implements ShiroLoginService { ...@@ -103,9 +103,9 @@ public class ShiroLoginServiceImpl implements ShiroLoginService {
// 生成新token字符串 // 生成新token字符串
String newToken = JwtUtil.generateToken(username, salt, Duration.ofSeconds(expireSecond)); String newToken = JwtUtil.generateToken(username, salt, Duration.ofSeconds(expireSecond));
// 生成新JwtToken对象 // 生成新JwtToken对象
JwtToken newJwtToken = JwtToken.build(newToken, username, salt, expireSecond); JwtToken newJwtToken = JwtToken.build(newToken, username, salt, expireSecond,jwtToken.getType());
// 更新redis缓存 // 更新redis缓存
loginRedisService.refreshLoginInfo(token, username, newJwtToken); sysLoginRedisService.refreshLoginInfo(token, username, newJwtToken);
log.debug("刷新token成功,原token:{},新token:{}", token, newToken); log.debug("刷新token成功,原token:{},新token:{}", token, newToken);
// 设置响应头 // 设置响应头
// 刷新token // 刷新token
......
...@@ -110,6 +110,13 @@ public class JwtUtil { ...@@ -110,6 +110,13 @@ public class JwtUtil {
return null; return null;
} }
/**
* 验证token
*
* @param token
* @param salt
* @return
*/
public static boolean verifyToken(String token, String salt) { public static boolean verifyToken(String token, String salt) {
try { try {
Algorithm algorithm = Algorithm.HMAC256(salt); Algorithm algorithm = Algorithm.HMAC256(salt);
......
...@@ -32,6 +32,9 @@ import java.util.Date; ...@@ -32,6 +32,9 @@ import java.util.Date;
@Accessors(chain = true) @Accessors(chain = true)
public class JwtTokenRedisVo implements Serializable { public class JwtTokenRedisVo implements Serializable {
private static final long serialVersionUID = 1831633309466775223L; private static final long serialVersionUID = 1831633309466775223L;
private String type;
/** /**
* 登录ip * 登录ip
*/ */
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
package io.geekidea.springbootplus.framework.util; package io.geekidea.springbootplus.framework.util;
import io.geekidea.springbootplus.config.constant.CommonRedisKey;
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.LoginSysUserRedisVo; import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
...@@ -55,7 +54,11 @@ public class LoginUtil { ...@@ -55,7 +54,11 @@ public class LoginUtil {
if (StringUtils.isBlank(username)) { if (StringUtils.isBlank(username)) {
return null; return null;
} }
return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, username)); // return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, username));
LoginSysUserRedisVo loginSysUserRedisVo =new LoginSysUserRedisVo();
loginSysUserRedisVo.setUsername(username);
return loginSysUserRedisVo;
} }
/** /**
......
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