五、权限管理
5.1 引入JWT
JWT是JSON Web Token的缩写,即JSON Web令牌,是一种自包含令牌。 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
JWT最重要的作用就是对 token信息的防伪作用。
在common-util模块中引入依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
创建工具类,用于生成token和根据token获取用户名和用户id。
package pers.beiluo.yunshangoffice.common.jwt;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* 此类用于完成与token相关的功能
* 例如:生成token或解析token等
*/
public class JWTHelper {
//token失效时间
private static long tokenExpiration = 365*24*60*60*1000;
//签名,根据签名进行加密
private static String tokenSignKey = "adcde";
//创建token
public static String createToken(Long userId,String username){
return Jwts
//创建JWT实例
.builder()
//设置类别,表示这个Jwt面向的用户
.setSubject("AUTH-USER")
//设置token的失效时间
.setExpiration(new Date(System.currentTimeMillis()+tokenExpiration))
//向claims属性中存值
.claim("userId",userId)
.claim("username",username)
//使用signkey进行签名
.signWith(SignatureAlgorithm.HS512,tokenSignKey)
//设置压缩方法
.compressWith(CompressionCodecs.GZIP)
//返回完整的JWT
.compact();
}
//获取用户id
public static Long getUserId(String token){
try {
if(StringUtils.isEmpty(token)){
return null;
}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims body = claimsJws.getBody();
//通过claim获取用户id
return ((Integer)body.get("userId")).longValue();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//获取用户名称
public static String getUsername(String token){
try {
if(StringUtils.isEmpty(token)){
return null;
}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims body = claimsJws.getBody();
return (String)body.get("username");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
5.2 用户登录
首先创建一个前后端数据交互的类:
@ApiModel("登录信息类")
@Data
public class LoginVo {
//用户名
@ApiModelProperty("用户名")
private String username;
//用户密码
@ApiModelProperty("密码")
private String password;
}
创建MD5密码加密工具类:
package pers.beiluo.yunshangoffice.common.jwt;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
public static void main(String[] args) {
System.out.println(MD5.encrypt("111111"));
}
}
修改IndexController中的方法:
@ApiOperation("登录")
@PostMapping("/login")
public Result login(@RequestBody LoginVo loginVo){
//根据用户名查询数据库
LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
sysUserLambdaQueryWrapper.eq(SysUser::getUsername,loginVo.getUsername());
SysUser one = sysUserService.getOne(sysUserLambdaQueryWrapper);
//判断该用户是否存在
if(null == one){
throw new CustomizedException(201,"用户尚未注册");
}
//判断密码是否正确
if(!one.getPassword().equals(MD5.encrypt(loginVo.getPassword()))){
throw new CustomizedException(201,"用户名或密码错误");
}
//判断用户是否有效
if(one.getStatus().intValue() == 0){
throw new CustomizedException(201,"用户已被禁用");
}
//走到这里说明用户验证成功,返回token
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("token", JWTHelper.createToken(one.getId(),one.getUsername()));
return Result.ok(stringObjectHashMap);
}
5.3 返回用户信息
//IndexController
@ApiOperation("获取用户信息")
@GetMapping("/info")
public Result getInfo(HttpServletRequest request){
//首先获取token
String token = request.getHeader("token");
//根据token获取用户id
Long userId = 1L;//JWTHelper.getUserId(token);
//获取用户信息
LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
sysUserLambdaQueryWrapper.eq(SysUser::getId,userId);
SysUser one = sysUserService.getOne(sysUserLambdaQueryWrapper);
//获取用户角色信息
Map<String, Object> roleOfUserById = sysUserService.getRoleOfUserById(userId);
ArrayList<SysRole> assignedRole = (ArrayList<SysRole>) roleOfUserById.get("assignedRole");
List<String> collect = assignedRole.stream().map(item -> item.getRoleName()).collect(Collectors.toList());
//根据用户id获取菜单信息
List<RouterVo> menuList = sysMenuService.getUserRouterList(userId);
//根据用户id获取按钮权限信息
List<String> permissionList = sysMenuService.getUserPermissionList(userId);
//创建返回对象
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("name",one.getName());
stringObjectHashMap.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
stringObjectHashMap.put("roles",collect);
stringObjectHashMap.put("buttons",permissionList);
stringObjectHashMap.put("routers",menuList);
return Result.ok(stringObjectHashMap);
}
//SysMenuServiceImpl.getUserRouterList
/**
* 根据用户id获取菜单的路由结构
* @param userId
* @return
*/
@Override
public List<RouterVo> getUserRouterList(Long userId) {
List<SysMenu> sysMenus = null;
//判断是否为管理员,如果为管理员则直接返回所有菜单,假设用户id为1的是管理员,其余都为普通用户
if(userId.longValue() == 1){
LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
sysMenuLambdaQueryWrapper.eq(SysMenu::getStatus,1);
//按照sort_value进行排序
sysMenuLambdaQueryWrapper.orderByAsc(SysMenu::getSortValue);
sysMenus = baseMapper.selectList(sysMenuLambdaQueryWrapper);
}else{
sysMenus = baseMapper.selectUserMenuList(userId);
}
//将用户拥有菜单转换成树形
List<SysMenu> sysMenus1 = MyUtils.buildMenu(sysMenus);
//创建返回对象,将树形菜单转换成路由形式
ArrayList<RouterVo> routerVos = MyUtils.buildRouter(sysMenus1);
return routerVos;
}
//SysMenuServiceImpl
/**
* 根据用户id查询用户拥有的按钮权限
* @param userId
* @return
*/
@Override
public List<String> getUserPermissionList(Long userId) {
List<SysMenu> sysMenus = new ArrayList<>();
//如果用户为管理员,则查询所有菜单
if(userId.longValue() == 1){
LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
sysMenuLambdaQueryWrapper.eq(SysMenu::getStatus,1);
sysMenus = baseMapper.selectList(sysMenuLambdaQueryWrapper);
}else{
sysMenus = baseMapper.selectUserMenuList(userId);
}
//从菜单列表中获取可以操作的按钮权限
List<String> collect = sysMenus.stream()
.filter(ele -> ele.getType() == 2)
.map(ele -> ele.getPerms())
.collect(Collectors.toList());
return collect;
}
<!--SysMenuMapper.selectUserMenuList,这个方法涉及多表联查,所以需要在xml文件中定义-->
<mapper namespace="pers.beiluo.yunshangoffice.mapper.SysMenuMapper">
<!--映射关系
在实体类中可以使用@TableField注解指定表字段与类字段的映射关系,在这里就可以开启自动映射
如果开启了驼峰命名,并且是按照规范命名的,那么就不需要使用@TableFiled注解,也不需要resultMap标签-->
<resultMap id="userMenuMap" type="pers.beiluo.yunshangoffice.model.system.SysMenu" autoMapping="true">
</resultMap>
<!--抽取列名-->
<sql id="columns">
sm.id,sm.parent_id,sm.name,sm.type,sm.path,sm.component,sm.perms,sm.icon,sm.sort_value,sm.status,sm.create_time,sm.update_time,sm.is_deleted
</sql>
<select id="selectUserMenuList" resultMap="userMenuMap">
SELECT DISTINCT <include refid="columns" />
FROM sys_role_menu srm INNER JOIN
sys_menu sm ON srm.menu_id=sm.id
INNER JOIN sys_user_role sur ON
srm.role_id=sur.role_id
WHERE sur.user_id=#{userId}
AND sm.status=1
AND srm.is_deleted=0
AND sm.is_deleted=0
AND srm.is_deleted=0
</select>
</mapper>