SpringBoot开发项目实战记录(三)
一、 根据请求的url判断角色
1.1 业务层Service
首先要拿到每个菜单对应的角色
/**
* 根据角色查菜单
* @return
*/
@Override
public List<Menu> getMenusWithRole() {
return menuMapper.getMenusWithRole();
}
1.2 mapper层
1. 查询的xml
<!--根据角色获取菜单列表-->
<select id="getMenusWithRole" resultMap="MenusWithRole">
SELECT
m.*,
r.id as rid,
r.`name` as rname,
r.nameZh AS rnameZh
FROM t_menu m,
t_menu_role mr,
t_role r
WHERE
m.id = mr.id
AND
mr.rid = r.id
ORDER BY m.id
</select>
2. 返回格式map (MenusWithRole)
<resultMap id="MenusWithRole" type="com.jzq.server.pojo.Menu" extends="BaseResultMap">
<collection property="roles" ofType="com.jzq.server.pojo.Role">
<id column="rid" property="id"></id>
<result column="rname" property="name"></result>
<result column="rnameZh" property="nameZh"></result>
</collection>
</resultMap>
3. url匹配权限过滤器
⭐知识点:
- 用于匹配的Spring类:AntPathMatcher antPathMatcher = new AntPathMatcher();
根据获取请求的url和菜单列表的url对比:antPathMatcher.match(requestUrl, menu.getUrl())- 如果匹配到,就把能用这个路径的角色放到数据返回
String[] strArray = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
return SecurityConfig.createList(strArray);
/**
* 权限控制
* 根据请求url,分析请求所需角色
*/
public class CustomFilter implements FilterInvocationSecurityMetadataSource {
@Autowired
private IMenuService menuService;
// 匹配url的实例
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
// 获取请求的Url
String requestUrl = ((FilterInvocation) o).getRequestUrl();
System.out.println("requestUrl"+requestUrl);
// 获取菜单信息
List<Menu> menuList = menuService.getMenusWithRole();
System.out.println("menuList"+menuList);
System.out.println("--------------------------");
for (Menu menu : menuList) {
// 逐个与url进行匹配
if (antPathMatcher.match(menu.getUrl(),requestUrl)){
// 如果匹配成功(把这个路径对应的角色封装到一个数组)
String[] strArray = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
return SecurityConfig.createList(strArray);
}
}
// 如果匹配不上,就默认返回登录权限的路径
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return false;
}
}
二、后端判断用户角色
2.1 pojo层内Admin加入角色字段
@ApiModelProperty(value = "角色")
@TableField(exist = false)
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = roles.stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
return authorities;
}
2.2 service层
/**
* 根据用户id获取角色权限
* @param adminId
* @return
*/
@Override
public List<Role> getRoles(Integer adminId) {
return roleMapper.getRoles(adminId);
}
2.3 mapper层
<select id="getRoles" resultType="com.jzq.server.pojo.Role">
SELECT
r.id,
r.`name`,
r.nameZh
FROM t_admin_role AS ar
LEFT JOIN t_role AS r ON ar.rid = r.id
where ar.adminId = #{adminId}
</select>
2.4 后端验证权限过滤器
package com.jzq.server.config.custom;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* 权限控制
* 判断用户角色
*/
@Component
public class CustomUrlDecisionManger implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute configAttribute : collection) {
// 当前url所需要的角色
String needRole = configAttribute.getAttribute();
// 判断角色是否登录即可访问的角色,此角色在CustomFilter中设置
if("ROLE_LOGIN".equals(needRole)) {
// 判断是否登录了
if(authentication instanceof AnonymousAuthenticationToken) {
throw new AccessDeniedException("尚未登录,请登录");
}else {
return;
}
}
// 判断角色是否为url所需要的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不足,请联系管理!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return false;
}
@Override
public boolean supports(Class<?> aClass) {
return false;
}
}
2.5 把过滤器设置到security配置类中
@Override
protected void configure(HttpSecurity http) throws Exception {
// 使用JWT,不需要csrf
http.csrf()
// 禁用csrf
.disable()
// 基于token, 不需要session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 允许登录访问
//.antMatchers("/login", "/logout")
//.permitAll()
// 除了上面的请求,其他的都要认证
.anyRequest()
.authenticated()
// 动态权限配置
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(customUrlDecisionManger);
o.setSecurityMetadataSource(customFilter);
return o;
}
})
.and()
// 禁用缓存
.headers()
.cacheControl();
}
三、职位管理功能
3.1 pojo实体类
⭐知识点:
@JsonFormat(pattern = “yyyy-MM-dd”, timezone = “Asia/Shanghai”)
从数据库获取日期类型,通过使用@JsonFormat可以很好的解决后台到前台时间格式转换(时间戳到格式化日期字符串),pattern:是你需要转换的时间日期的格式,timezone:是时间设置为东八区,避免时间在转换中有误差前端
@DateTimeFormat(pattern = "yyyy-MM-dd") 传给后端存库时可以使用@DataTimeFormat便很好的解决了这个问题,接下来记录一下具体的@JsonFormat与DateTimeFormat的使用过程。
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
private LocalDateTime createDate;
3.2 职位管理Controller(通过MybatisPlus操作)
⭐知识点:
- @Autowired
private IPositionService positionService; 注入service- 一些MybatisPlus方法的使用:
1.获取所有记录:positionService.list()
2.增加一条记录:positionService.save(position)
3.更新一条记录:positionService.updateById(position)
4.删除一条记录:positionService.removeById(id)
5.删除一组记录:positionService.removeByIds(Arrays.asList(ids))
/**
* <p>
* 前端控制器
* </p>
*
* @author seven
* @since 2022-01-02
*/
@RestController
@Api(tags = "职位管理接口")
@RequestMapping("/api/system/config/pos")
public class PositionController {
@Autowired
private IPositionService positionService;
@ApiOperation(value = "获取所有职位信息")
@GetMapping("/")
public List<Position> getAllPosition() {
// MybatisPlus的方法,获取所有记录
return positionService.list();
}
@ApiOperation(value = "增加一个职位")
@PostMapping("/")
public RespBean addPosition(@RequestBody Position position) {
position.setCreateDate(LocalDateTime.now());
// MybatisPlus的方法,增加一条记录
if(positionService.save(position)) {
return RespBean.success("增加职位成功");
}
return RespBean.warning("增加职位失败");
}
@ApiOperation(value = "修改职位")
@PutMapping("/")
public RespBean updatePosition(@RequestBody Position position){
// MybatisPlus的方法,修改一条记录
if (positionService.updateById(position)) {
return RespBean.success("修改职位信息成功");
}
return RespBean.warning("修改职位信息失败");
}
@ApiOperation(value = "删除一个职位信息")
@DeleteMapping("/{id}")
public RespBean deletePosition(@PathVariable Integer id) {
if(positionService.removeById(id)) {
return RespBean.success("删除一个职位成功");
}
return RespBean.warning("删除一个职位失败");
}
@ApiOperation(value = "删除一组职位信息")
@DeleteMapping("/")
public RespBean deletePositions(Integer[] ids) {
if(positionService.removeByIds(Arrays.asList(ids))) {
return RespBean.success("删除一组职位成功");
}
return RespBean.error("删除一组职位失败");
}
}
示例: