jwt
这里介绍一下 jwt 的使用。
功能介绍
主要包含在 wmeimob-spring-boot-starter-jwt
jar 中。基于 spring security 实现。
主要功能包含如下两方面:
- 鉴权
- 授权
- 踢人
鉴权
利用 spring security 的能力,提供了如下的配置项,来决定是否忽略鉴权:
yml
permissionUrls:
- xxx
- xxx
参看标品配置可以知道,permissionUrls 配置的地址是不需要填写 context-path,这是因为框架自动去读取了该配置,而如果手动加了,反而会出现错误。
yml
server:
context-path: /wechat
同时系统默认提供拦截忽略的 api 列表,如下:
yml
/favicon.ico,
/login,
/api/sso/**,
/api/**/open/**
/api/**/inside/**
/static/**
/doLogin
error
druid/**
dev/login
//swagger---start-----
doc.html
webjars/**
swagger-resources
v2/**
public/**
//swagger---end-----
actuator/**
未标记放行的则需要进行鉴权,而鉴权需要设置如下重要参数:
yml
expiration = # 默认 604800 秒(7天)
secret = # 密钥,为空的话,框架默认每次都生成一个密钥
我们知道,jwt token 一旦颁发后无法让其失效,如果硬要让它失效呢?
没错,就是黑名单方案,结合 redis 来实现。
授权
对于后台访问,我们需要赋与权限才能访问。
踢人
基于 redis 黑名单实现。与用户退出原理一致。
与标品的集成
java
/**
* 租户用户接口
*
* @author mjyang
* @date 2022/3/29 18:11
*/
public interface TeancyUser {
Long getUserId();
String getUsername();
String getUserMobile();
boolean isAnonymousUser();
Long getAdminUserId();
String getAdminUserName();
boolean getUserDisableStatus();
Map<String, Object> getExtendInfo();
}
java
/**
* 管理后台租户用户实现类
*
* @author mjyang
* @date 2022/3/29 18:13
*/
public class AdminTeancyUser extends BaseTeancyUser {
private SysUser getUser() {
SysUserDetails sysUserDetails = (SysUserDetails) this.getPrincipal();
return sysUserDetails.getSysUser();
}
@Override
public Long getUserId() {
return this.getUser().getId();
}
@Override
public String getUsername() {
return this.getUser().getUsername();
}
@Override
public String getUserMobile() {
return this.getUser().getMobile();
}
@Override
public Map<String, Object> getExtendInfo() {
SysUser sysUser = this.getUser();
Map<String, Object> extendMap = new HashMap<>();
extendMap.put("lastLoginDate", sysUser.getLastLoginDate());
extendMap.put("email", sysUser.getEmail());
extendMap.put("realName", sysUser.getUsername());
extendMap.put("salt", sysUser.getSalt());
extendMap.put("password", sysUser.getPassword());
extendMap.put("mobile", sysUser.getMobile());
return extendMap;
}
}
java
/**
* 前台租户用户实现类
*
* @author mjyang
* @date 2022/3/29 18:13
*/
public class MemberTeancyUser extends BaseTeancyUser {
private MemBaseInfo getUser() {
MemberUserDetails memberDetails = (MemberUserDetails) this.getPrincipal();
return memberDetails.getMember();
}
@Override
public Long getUserId() {
return this.getUser().getId();
}
@Override
public String getUsername() {
return this.getUser().getNickName();
}
@Override
public String getUserMobile() {
return this.getUser().getMobile();
}
@Override
public boolean getUserDisableStatus() {
return ObjectUtil.equal(EntityConstant.INVALID, this.getUser().getStatus());
}
@Override
public Map<String, Object> getExtendInfo() {
MemBaseInfo memBaseInfo = this.getUser();
Map<String, Object> extendMap = new HashMap<>();
extendMap.put("headImg", memBaseInfo.getHeadImg());
extendMap.put("openId", memBaseInfo.getOpenId());
extendMap.put("mobile", memBaseInfo.getMobile());
return extendMap;
}
}
通过注入 TeancyUser 接口,我们可以灵活的获取到登录用户信息。注意,放行的 api 是取不到用户信息的,通过 TeancyUser 接口获取发现会报错 java.lang.String cannot be cast to xxxx,除了调整源码外,还可以通过如下方式去获取
java
/**
* 管理后台租户用户实现类
*
* @author mjyang
* @date 2022/3/29 18:13
*/
public class AdminTeancyUser extends BaseTeancyUser {
private SysUser getUser() {
if (isAnonymousUser()) {
return new SysUser();
}
SysUserDetails sysUserDetails = (SysUserDetails) this.getPrincipal();
return sysUserDetails.getSysUser();
}
}
java
/**
* 前台租户用户实现类
*
* @author mjyang
* @date 2022/3/29 18:13
*/
public class MemberTeancyUser extends BaseTeancyUser {
private MemBaseInfo getUser() {
if (isAnonymousUser()) {
return new MemBaseInfo();
}
MemberUserDetails memberDetails = (MemberUserDetails) this.getPrincipal();
return memberDetails.getMember();
}
}
java
/**
* 快速获取用户信息工具类
*
* @author mjyang
* @date 2023/3/31 9:27
*/
@UtilityClass
public class SecurityFrameworkUtils {
public Long getUserId(HttpServletRequest request) {
return MapUtil.getLong(getUserInfo(request), "id");
}
public Map<String, Object> getUserInfo(HttpServletRequest request) {
String authToken = request.getHeader(HttpHeaders.AUTHORIZATION);
if (StrUtil.isEmpty(authToken)) {
return MapUtil.empty();
}
JwtTokenUtil jwtTokenUtil = SpringUtil.getBean(JwtTokenUtil.class);
DecodedJWT decode = jwtTokenUtil.decode(authToken);
String userId = jwtTokenUtil.getUserIdFromSubject(decode.getSubject());
try {
jwtTokenUtil.verify(authToken, userId);
return MapUtil.of("id", userId);
} catch (JWTVerificationException e) {
if ((e instanceof TokenExpiredException)) {
return MapUtil.of("id", userId);
}
// ignore
}
return MapUtil.empty();
}
}