Java-Secruity-2

发布时间:2023年12月21日

?可以先看这篇文章

Secruity-1👈

1、授权

1.1 权限管理

在日常使用的系统中都会涉及到权限相关的操作,管理员有管理员的操作,用户有用户的操作,不同的用户可以使用不同的功能,这需要使用到权限管理。

所以在写接口的时候需要在后台进行用户权限的判断,判断当前用户是否有相应的权限,必须具有所需权限才能进行相应的操作。

image-20221104151126316

RBAC权限模型

user表

menu表

role表

role_menu表

user_role表
?sql语句

通过user_id关联user_role得到用户的role_id

通过role_id关联role得到角色对应的信息

通过role_id关联role_menu得到menu_id

通过menu_id关联menu得到权限的具体信息

2.2 授权实现

2.2.1 设置资源权限

  • 启动类开启配置
    @EnableGlobalMethodSecurity(prePostEnabled = true)
  • 注解配置
    @RequestMapping
    // 设置这个接口需要system:class:list权限,这个可以在数据库的sys_menu中查找
    @PreAuthorize("hasAnyAuthority('在数据库设置的权限标识')")
    public String queryAll() {
     ? ?System.out.println("queryAll");
     ? ?return "user";
    }
    重写LoginUser里的getAuthorities
  • // 存储每个用户的权限信息
        private List<String> permissions;
    
    
    
        @JsonIgnore// 不手动添加的话后去反序列化会出现异常
        /*
        authorities用于在底下的getAuthorities返回
        登入成功后,会把用户信息存在redis里java的数据是以对象的形式表示
        redis的数据表示格式和java不一样,需要进行数据格式的转化
         */
                // 权限集合需要转换成这种类型的Security才能判断
        List<GrantedAuthority> authorities = new ArrayList<>();
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
    
            //现在要做的就是把上面定义的authorities放入每个用户的权限信息
            for(String permission:permissions) {
                authorities.add(new SimpleGrantedAuthority(permission));
            }
            return authorities;
        }
    mapper
  • public interface UserMapper extends BaseMapper<MsUser> {
    
        List<String> getPermissions(Long userId);
    
    }
    xml
  • <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.demo.mapper.UserMapper">
    
        <select id="getPermissions" resultType="string">
            SELECT t5.key FROM user t1
                JOIN user_role t2 on t1.id = t2.user_id
                JOIN role t3 ON t3.role_id = t2.role_id
                JOIN role_menu t4 ON t4.role_id = t3.role_id
                JOIN menu t5 ON t5.menu_id = t4.menu_id
            WHERE t1.id = #{userId}
        </select>
    
    
    </mapper>
    
    UserDetailsServiceImpl中获取权限信息
  • 
    
    // 重写了UserDetailsService,控制台就没有打印生成的密码。因为我们自定义了登录流程
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private IUserService userService;
        @Autowired
        private UserMapper userMapper;
    
        // UserDetails: security存放登录用户信息
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            System.out.println("loadUserByUsername");
            LambdaQueryWrapper<MsUser> qw = new LambdaQueryWrapper<>();
            qw.eq(MsUser::getUsername, username);
            // 根据账号查询用户信息
            MsUser msUser = userService.getOne(qw);
    
            // TODO: 统一处理异常
            if(msUser == null) {
                throw new RuntimeException("账号不存在");
            }
    
            LoginUser loginUser = new LoginUser();
            loginUser.setMsUser(msUser);
    
            //这里底下是登录成功要做的事
            
            // 获取权限信息
            List<String> permissions = userMapper.getPermissions(msUser.getId());
            // 将权限信息装到loginUser对象
            loginUser.setPermissions(permissions);
    
            return loginUser;
        }
    
    }
    
    自定义权限校验
  • @Component("ss")
    //类名方法名怎么定义都行
    public class MyExpressionUtil {
    
        //判断当前用户有没有当前方法上的标识
        public boolean myAuthority(String key) {
    
            //SecurityContextHolder.getContext()获取用户信息
            // 获取用户的权限列表
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    
            // 获取到登录的用户
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
    
            // 拿到该用户的权限
            List<String> permissions = loginUser.getPermissions();
    
            return permissions.contains(key);
        }
    
    
    控制层的方法上加上注解
  •  @GetMapping("/test1")
        @PreAuthorize("@ss.myAuthority('t1')")
        public String test1() {
            System.out.println("test1");
            return "test1";
        }

    3.1 简介

    前面写的接口,如果有错误异常等,前端看不见也不知道什么问题,所以一般会做一个统一的错误处理,可以使用SpringSecurity的异常处理机制。

    在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。

    如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。

    如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。

    所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPointAccessDeniedHandler然后配置给SpringSecurity即可。

统一返回数据Result?

@Data
public class R<T> {

    private String msg;

    private Integer code;

    private T data;

    public R() {
    }

    public R(String msg, Integer code, T data) {
        this.msg = msg;
        this.code = code;
        this.data = data;
    }

    public static R success() {
        return new R("操作成功!!", 200, null);
    }

    public static <T> R success(T data) {
        return new R("操作成功!!", 200, data);
    }

    public static R error() {
        return new R("操作失败!!", 500, null);
    }

    public static R error(String msg, Integer code) {
        return new R(msg, code, null);
    }

    public static R to(int rs) {
        return rs > 0 ? R.success() : R.error();
    }
    
}

捕捉认证失败

//捕捉认证过程出现的异常(token异常)
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf8");
        response.getWriter().write(JSON.toJSONString(R.error("认证失败", 401)));
    }
}

捕捉授权失败

//捕捉授权失败的异常(权限异常)
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf8");
        response.getWriter().write(JSON.toJSONString(R.error("没有权限", 403)));
    }
}
3.3 配置给SpringSecurity

修改configure方法(Security配置的过滤器都要加到这里)

@Autowired
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Autowired
private AccessDeniedHandlerImpl accessDeniedHandler;
?
// 配置异常处理器
http
 ?  .exceptionHandling()
 ?  .authenticationEntryPoint(authenticationEntryPoint)
 ?  .accessDeniedHandler(accessDeniedHandler);

文章来源:https://blog.csdn.net/2302_76649176/article/details/135136355
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。