Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

RESTFul API

主要介绍后端 API 的角色权限控制。

数据库设计

数据库主要包含五张表,分别是用户表 user、角色表 role、用户角色表 user_role、权限表 permission、角色权限表 role_permission。

数据库关系模型如下:

user 表:用户信息。

role 表:角色信息。

user_role 表:用户对应的角色,一对一。

permission 表:权限能操作的资源以及操作方式。

role_permission 表:角色所对应的权限,一对多。

为什么 ROLE_ADMIN 角色在数据库没有权限?

ROLE_ADMIN 作为超级管理员这类角色,应该是具有所有权限的,但是对于数据库来说,没必要保存所有权限,只要在查询到该角色时返回所有权限即可。

角色权限控制

Spring Security + Json Web Token 鉴权:

最终效果,在控制器上的注解:

@PreAuthorize("hasAuthority('user:list')")

实现思路:用户登录 -> 服务端生成 token -> 客户端保存 token,之后的每次请求都携带该 token,服务端认证解析。

所以在服务端认证解析的 token 就要保存有用户的角色和相应的权限:

// service/impl/UserDetailsServiceImpl.java // 为了方便,角色和权限都放在一起 // 权限 List<SimpleGrantedAuthority> authorities = user.getPermissionCodeList().stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); // 角色 authorities.add(new SimpleGrantedAuthority(user.getRoleName())); // [ROLE_TEST, role:list, user:list]

JWT 生成 token:

// core/jwt/JwtUtil.java Jwts.builder() // 设置用户名 .setSubject(username) // 添加权限属性 .claim(this.AUTHORITIES_KEY, authorities) // 设置失效时间 .setExpiration(date) // 私钥加密生成签名 .signWith(SignatureAlgorithm.RS256, privateKey) .compact();

Base64 解码 JWT 生成的 token:

{"alg":"RS256"}{"sub":"test","auth":"ROLE_TEST,role:list,user:list,"exp":1519742226}<wZJ69e���,x	옮J܃a} @ϋ+sˆvफ़t�|Tq |7uƙ 

之后的控制器就可以使用 hasAuthority 和 hasRole 注解控制权限访问了:

@PreAuthorize("hasRole('ROLE_ADMIN') or hasAuthority('user:list')")

axios 预请求和跨域

由于前后端分离,会出现跨域问题,参考跨域资源共享 CORS 详解

// core/jwt/JwtAuthenticationFilter.java // 解决跨域问题 response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With"); // 明确允许通过的方法,不建议使用* response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Expose-Headers", "*"); // 预请求后,直接返回 // 返回码必须为 200 否则视为请求失败 if (request.getMethod().equals("OPTIONS")) { return; }