美文网首页程序员Spring Boot
基于Spring Boot 的REST服务分级授权

基于Spring Boot 的REST服务分级授权

作者: 王不哈 | 来源:发表于2018-01-17 23:05 被阅读447次

接上篇文章:基于Spring Boot 的RESTful权限认证

在实际项目中,用户通常有多个角色,并且不同的角色对资源的访问有不同的权限。
在项目中增加一个UserController并增加获得所有用户信息的rest服务:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @Authorization
    @RequestMapping(value = "all")
    public ResponseEntity findAll(@PageableDefault(value = 10) Pageable pageable) {
        Page<UserEntity> users = userService.findAllUser(pageable);
        return new ResponseEntity<>(ResultEntity.ok("查询成功", users), HttpStatus.OK);
    }
}

但是该接口并非所有用户都可调用,只有类似系统管理员角色的用户才有权限访问。

实现思路:

  1. UserEntity.java中增加一个代表用户权限的属性(用数字表示,数字越大,代表权限越高);
  2. Authorization注解增加一个属性,记录访问该rest接口至少需要的权限;
  3. 用户登陆之后缓存token时,同时将用户的权限等级(数字)缓存;
  4. AuthorizationInterceptor拦截器中,对比除了校验用户登陆状态之外,还判断当前用户的权限是否不低于(数字是否大于等于)当前访问的rest接口所需的权限(从Authorization注解中获取)。

实现
1. 增加用户类型

public class UserType {
    public static final int DEFAULT = 0;   //普通用户
    public static final int ADMINISTRATOR = 1;  //权限更高的管理员账户
}
@Data
@Entity
@Table(name = "user")
public class UserEntity {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    @Column(name = "user_id", unique = true, nullable = false, length = 32)
    private String userId;

    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createDate;

    private String userName;
    private String password;
    /**
     * UserEntity中增加字段,记录用户的权限,默认为普通用户
     */
    private int userType = UserType.DEFAULT;
    private boolean dr;
}

2. 注解增加属性

/**
 * @author sukaiyi
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {
    int level() default UserType.DEFAULT;  //此属性表示访问rest方法时,至少需要的用户权限等级
}

3. 在需要更高权限访问的rest方法上

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    /**
     * 该方法需要用户至少有ADMINISTRATOR的权限
     */
    @Authorization(level = UserType.ADMINISTRATOR)
    @RequestMapping(value = "all")
    public ResponseEntity findAll(@PageableDefault(value = 10) Pageable pageable) {
        Page<UserEntity> users = userService.findAllUser(pageable);
        return new ResponseEntity<>(ResultEntity.ok("查询成功", users), HttpStatus.OK);
    }
}

4. 用户登陆之后将用户的权限信息和token一并存入缓存
为了方便,这里直接在TokenEntity中新建了一个UserEntity的属性,这样自然将用户的权限信息包含进去了。(缓存时直接缓存TokenEntity对象)

/**
 * @author sukaiyi
 */
@Data
public class TokenEntity {
    private UserEntity user;
    private String token;
}

5. 改造AuthorizationInterceptor拦截器

/**
 * @author sukaiyi
 */
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private TokenService tokenService;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        Authorization authorization = method.getAnnotation(Authorization.class);
        if (authorization == null) {
            return true;
        }
        String auth = request.getHeader(Constants.AUTHORIZATION);
        TokenEntity tokenEntity = tokenService.getToken(auth);
        //将authorization一并传入检查
        if (tokenService.checkToken(tokenEntity, authorization)) {
            request.setAttribute(Constants.CURRENT_USER_ID, tokenEntity.getUser().getUserId());
            return true;
        } else {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
    }
}

tokenService

    @Override
    public boolean checkToken(TokenEntity entity, Authorization authorization) {
        if (entity == null) {
            return false;
        }
        TokenEntity token = redisTemplate.opsForValue().get(entity.getUser().getUserId());
        if (token == null) {
            return false;
        }
        if (StringUtils.isEmpty(token.getToken())) {
            return false;
        }
        if (!token.getToken().equals(entity.getToken())) {
            return false;
        }
        //若需要的权限大于当前用户的权限,则授权失败
        if (authorization.level() > token.getUser().getUserType()) {
            return false;
        }
        return true;
    }

注:这种多级授权方式,UserType中数字越大,权限越高。

相关文章

网友评论

    本文标题:基于Spring Boot 的REST服务分级授权

    本文链接:https://www.haomeiwen.com/subject/fdjeoxtx.html