User实体类(用户)
@ApiModel("用户实体") @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; @ApiModelProperty(value = "昵称") private String name; @ApiModelProperty(value = "用户名") private String username; private String password; private Double balance; /** * 存储用户所有的菜单信息 */ @TableField(exist = false) Set<String> menuUrlList; // @TableField(exist = false) // Set<String> menuAuthStrList; public static void main(String[] args) { User user = new User(); user.setName("xxxx"); System.out.println(user); } }
Role实体类 (角色)
@Data public class Role{ private Long id; private String name; }
Menu实体类(菜单)
public class Menu{ private Long id; private String name; private String url; private String authStr; private Long parentId; }
RoleMapper
public interface RoleMapper extends BaseMapper<Role> { }
MenuMapper
public interface MenuMapper extends BaseMapper<Menu> { }
MenuService
public interface MenuService extends IService<Menu> { Set<String> listUrlByUserId(Long id); }
MenuServiceImpl
注:这里本该是多对多连表查询到数据,这里用add模拟查询到的数据
@Service public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService { @Override public Set<String> listUrlByUserId(Long id) { // 1.根据用户ID查询到所有的角色ID user-role // 2.根据所有角色ID查询到所有的菜单ID=>所有的菜单路径信息 role-menu // 总结:通过一条sql也是可以实现。 Set<String> menuUrlList = new HashSet<>(); // 模拟数据查询 menuUrlList.add("/"); menuUrlList.add("/index"); menuUrlList.add("/testTr"); return menuUrlList; } }
UserController(用户登录)
LoginInterceptor拦截器
逻辑关系
权限字符串,一个权限字符串对应多个接口
注解类
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface HasAuth { /** * 权限字符串 * @return */ String value() default ""; }
User实体类
@ApiModel("用户实体") @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; @ApiModelProperty(value = "昵称") private String name; @ApiModelProperty(value = "用户名") private String username; private String password; private Double balance; @TableField(exist = false) Set<String> menuAuthStrList; public static void main(String[] args) { User user = new User(); user.setName("xxxx"); System.out.println(user); } }
UserController登陆成功后
if(userDb != null){ // 获取用户所有的权限信息 Set<String> menuAuthList = menuService.listAuthStrByUserId(userDb.getId()); userDb.setMenuAuthStrList(menuAuthList); // 登陆成功了 String token = TokenUtil.generateToken(userDb); map.put("code",1); map.put("data",userDb); map.put("token",token); // session.setAttribute("username",userDb.getUsername()); }else{ map.put("msg","用户名或密码错误!"); }
MenuServicepublic interface MenuService extends IService<Menu> { Set<String> listAuthStrByUserId(Long id); }
MenuServiceImpl@Service public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService { @Override public Set<String> listAuthStrByUserId(Long id) { // 2.根据所有角色ID查询到所有的菜单ID=>所有的菜单路径信息 role-menu // 总结:通过一条sql也是可以实现。 Set<String> menuAuthStrList = new HashSet<>(); // 模拟数据查询 menuAuthStrList.add("hello:select"); return menuAuthStrList; } }
拦截器
if (!TokenUtil.verify(token)) { // 未登录跳转到登录界面 throw new RuntimeException("no login!"); } else { //通过反射拿到注解 HandlerMethod handlerMethod = (HandlerMethod) handler; HasAuth hasAuth = handlerMethod.getMethodAnnotation(HasAuth.class); if (hasAuth != null){ /** * 登陆验证通过=>验证权限信息 */ //根据token拿到user User user = TokenUtil.getUser(token); //根据user拿到权限字符串 Set<String> menuAuthStrList = user.getMenuAuthStrList(); //如果接口的注解不在user的权限中说明权限不足 if(!menuAuthStrList.contains(hasAuth.value())){ // 权限不足进制访问 throw new RuntimeException("403 forbidden!"); } } return true; }
这个相当于一个注解是一个权限字符串,一个注解可以表示多个接口,只需要在方法的上面加注解,使用这个接口的时候就会将这个的注解通过反射的方式拿到,然后用户权限验证,看看这个反射的权限字符串在不在用户的菜单权限字段中,如果在就说明有权限