当前位置: 首页 > news >正文

springboot—— Shiro实现认证和授权功能

一、数据库模板设计

在本文中,我们使用RBAC(Role-Based Access Control,基于角色的访问控制)模型设计用户,角色和权限间的关系。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系

一个用户对应一个或者多个角色。
一个角色对应一个或者多个权限。
一个权限对应能够访问对应的API或url资源。

 

1 . RBAC基本实体关系,Permission类(权限资源) :


/**
 * Created by EalenXie on 2019/3/25 11:15.
 * <p>
 * 权限许可(Permission) 操作 及其能访问url 权限对应一个url地址
 */
@Entity
@Table(name = "system_shiro_permission")
public class Permission extends BaseEntity {
    @Column(unique = true)
    private String name;                //权限名 唯一
    @Column(unique = true)
    private String url;                 //访问地址信息 唯一
    private String description;         //描述信息
    //省略getter/setter
}

 

2 . Role类(用户角色),一个角色拥有一个或者多个权限 :


/**
 * Created by EalenXie on 2019/3/25 11:18.
 * <p>
 * 角色(Role) 角色下面对应多个权限
 */
@Entity
@Table(name = "system_shiro_role")
public class Role extends BaseEntity {

    @Column(unique = true)
    private String name;                    //角色名 唯一
    private String description;             //描述信息
    @ManyToMany(fetch= FetchType.EAGER)
    private List<Permission> permissions;   //一个用户角色对应多个权限
    //省略getter/setter
}

 

3 . User类(用户),一个用户拥有一个或者多个角色 :


/**
 * Created by EalenXie on 2019/3/25 11:01.
 * <p>
 * 用户表(User) 用户下面对应多个角色
 */
@Entity
@Table(name = "system_shiro_user")
public class User extends BaseEntity {
    @Column(unique = true)
    private String username;//用户名 唯一
    private String password;//用户密码
    private String passwordSalt;//用户密码加密盐值
    @ManyToMany(fetch = FetchType.EAGER)
    private List<Role> roles;//用户角色  一个用户可能有一个角色,也可能有 多个角色
    //省略getter/setter
}

以上是对应关系的实体,数据库字段根据上述实体创建,这里就不写出来了。设计到的查询根据实际情况自己写,这里主要是讲shiro

二:Shiro整合实现思路

我们先来屡一下思路,实现认证权限功能主要可以归纳为3点:

1.定义一个ShiroConfig配置类,配置 SecurityManager Bean , SecurityManager为Shiro的安全管理器,管理着所有Subject;

2.在ShiroConfig中配置 ShiroFilterFactoryBean ,它是Shiro过滤器工厂类,依赖SecurityManager ;

3.自定义Realm实现类,包含 doGetAuthorizationInfo()doGetAuthenticationInfo()方法 

下面我们就来实现这些步骤:

 1、定义ShiroConfig配置类

/**
 * Shiro配置
 */
@Configuration
public class ShiroConfig {
    @Resource
    private OAuth3Realm userAuthRealm;
    /**
     * 配置安全管理器
     * @param oAuth3Realm UserRealm
     * @return DefaultWebSecurityManager
     */
    @Bean
    public SecurityManager securityManager(OAuth3Realm oAuth3Realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(oAuth3Realm);
        return securityManager;
    }

    /**
     * 配置Shiro过滤器工厂
     * 配置 资源访问策略 . web应用程序 shiro核心过滤器配置
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, UrlConfig urlConfig) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 注册安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 定义资源访问规则
        Map<String, String> filterMap = new LinkedHashMap<String, String>(16);
        /*
         * 过滤器说明
         * anon:不需要认证就可以访问的资源
         * authc:需要登录认证才能访问的资源
         * perms:需要指定权限才能访问的资源
         */
        // 需要登录认证才能访问的资源
        //oauth过滤,anon:不需要认证就可以访问的资源
        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("oauth3", new OAuth3Filter());
        // TODO 增加新的B端用户拦截器
        shiroFilterFactoryBean.setFilters(filters);
        //排除配置
        if (urlConfig.getAnonUrl() != null && !urlConfig.getAnonUrl().isEmpty()) {
            for (String anonUrl : urlConfig.getAnonUrl()) {
                filterMap.put(anonUrl, "anon");
            }
        }
        //对所有用户认证 对B端访问路由进行拦截
        filterMap.put("/**", "oauth3");

        //配置 拦截过滤器链
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启shiro 注解支持. 使以下注解能够生效 :
     * 需要认证 {@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}
     * 需要用户 {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}
     * 需要访客 {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}
     * 需要角色 {@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}
     * 需要权限 {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}
     * 启用shiro注解
     *加入注解的使用,不加入这个注解不生效
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 配置shiro中缓存管理器对象 (对象的名字不能写cacheManager,因为spring容器中已经存在一个名字为cacheManager的对象了)
     *
     * 作用:缓存授权信息,当第一次访问授权时,会调用自定义Realm的获取授权数据的方法,从数据库中查询授权数据,并将其以登录者的Principal为键,存储在缓存中。
     * 以后的每次访问授权,就直接从缓存中获取,而不再从数据库中获取。
     */
    @Bean
    public CacheManager shiroCacheManager() {
        return new MemoryConstrainedCacheManager();
    }
    /**
     * 配置记住我管理对象:底层同cookie对象将用户信息写到客户端
     */
    @Bean
    public RememberMeManager rememberMeManager() {
        CookieRememberMeManager cManager = new CookieRememberMeManager();
        //配置cookie
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        //设置cookie执行时长(7天)
        cookie.setMaxAge(1 * 24 * 60 * 60);
        cManager.setCookie(cookie);
        return cManager;

    }
 // @Bean
 //   public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
//        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
   //     advisorAutoProxyCreator.setProxyTargetClass(true);
   //     return advisorAutoProxyCreator;
   // }
}

注意:(当时笔者遇到的一个小问题,贴出来给大家涨姿势)

注解无效,登录时不会执行验证角色和权限的方法,只会执行登录验证方法,遂查询资料,得知shiro在subject.login(token)方法时不会执行doGetAuthorizationInfo方法,只有在访问到有权限验证的接口时会调用查看权限,于是猜想注解无效,发现shiro的权限注解需要开启才能有用,添加在配置文件中加入advisorAutoProxyCreatorgetAuthorizationAttributeSourceAdvisor两个bean开启shiro注解,解决问题。

 

shiroFilterFactoryBean 的Url配置

可以配置在数据库,也可以配置在配置文件,作者这里配置在配置文件中的

 

@Data
@Configuration
@ConfigurationProperties(prefix = "amsh")
public class UrlConfig {
    /**
     * 不需要拦截的url
     */
    private List<String> anonUrl;

    /**
     * openapi分页大小
     */
    private Integer openPageSize;
}

 

通过这种方式排除配置

注意:

1.这里要用LinkedHashMap 保证有序

2.filterChain基于短路机制,即最先匹配原则,

3.像anon、authc等都是Shiro为我们实现的过滤器

附录:

1.Shiro拦截机制表
Filter NameClassDescription
anonorg.apache.shiro.web.filter.authc.AnonymousFilter匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例/static/**=anon
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter基于表单的拦截器;如/**=authc,如果没有登录会跳到相应的登录页面登录
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilterBasic HTTP身份验证拦截器
logoutorg.apache.shiro.web.filter.authc.LogoutFilter退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/),示例/logout=logout
noSessionCreationorg.apache.shiro.web.filter.session.NoSessionCreationFilter不创建会话拦截器,调用subject.getSession(false)不会有什么问题,但是如果subject.getSession(true)将抛出DisabledSessionException异常
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例/user/**=perms["user:create"]
portorg.apache.shiro.web.filter.authz.PortFilter端口拦截器,主要属性port(80):可以通过的端口;示例/test= port[80],如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilterrest风格拦截器,自动根据请求方法构建权限字符串;示例/users=rest[user],会自动拼出user:read,user:create,user:update,user:delete权限字符串进行权限匹配(所有都得匹配,isPermittedAll)
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter角色授权拦截器,验证用户是否拥有所有角色;示例/admin/**=roles[admin]
sslorg.apache.shiro.web.filter.authz.SslFilterSSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口443;其他和port拦截器一样;
userorg.apache.shiro.web.filter.authc.UserFilter用户拦截器,用户已经身份验证/记住我登录的都可;示例/**=user
oauth2过滤器

-上面配置文件中加入的类- filters.put("oauth3", new OAuth3Filter());


/**
 * oauth2过滤器
 */
@Slf4j
public class OAuth3Filter extends AuthenticatingFilter {

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {

        //获取请求token
        String token = getRequestToken((HttpServletRequest) request);
        if (StringUtils.isEmpty(token)) {
        	//清理线程变量
        	ThreadLocalUserInfo.remove();
            return null;
        }
        return new OAuth3Token(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token,如果token不存在,直接返回401
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String token = getRequestToken(httpRequest);
        if (StringUtils.isEmpty(token)) {
            log.info("OAuth3Filter过滤器,onAccessDenied获取token:{}", token);
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=UTF-8");
            UnifiedResult result = UnifiedResult.fail(HttpStatus.UNAUTHORIZED.value(), "无效的token");
            String json = JSON.toJSONString(result);
            httpResponse.getWriter().print(json);
            return false;
        }
        return executeLogin(request, response);
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=UTF-8");
        try {
        	//清理线程变量
        	ThreadLocalUserInfo.remove();
            //处理登录失败的异常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            UnifiedResult result = UnifiedResult.fail(HttpStatus.UNAUTHORIZED.value(), throwable.getMessage());
            String json = JSON.toJSONString(result);
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return false;
    }

    /**
     * 获取请求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest) {
        //从header中获取token
        String token = httpRequest.getHeader("auth");
        //如果header中不存在token,则从参数中获取token
        if (StringUtils.isEmpty(token)) {
            token = httpRequest.getParameter("auth");
        }
        return token;
    }

}

 

2、实现自定义Realm类

自定义Realm类需要继承 AuthorizingRealm 类,实现 doGetAuthorizationInfo()和doGetAuthenticationInfo()方法即可 ,

doGetAuthorizationInfo() 方法是进行授权的方法,获取角色的权限信息

doGetAuthenticationInfo()方法是进行用户认证的方法,验证用户名和密码

/**
 * @ClassName MyShiroRealm
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/2/29 3:08
 * @Version 1.0
 */
@Service
public class MyShiroRealm  extends AuthorizingRealm {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private RolePermissionMapper rolePermissionMapper;

    /**
     * 获取用户角色和权限
     * @param principal
  * 授权(验证权限时调用)
     * 获取授权信息(把数据库中shiroID加入到shiro中管理,然后如果ctronler层方法加上 @RequiresPermissions注解才会调用这个方法和数组进行比对)
     * 只有当需要检测用户权限的时候才会调用此方法
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        if(principal == null){
            throw new AuthorizationException("principals should not be null");
        }
        User userInfo= (User) SecurityUtils.getSubject().getPrincipal();
        System.out.println("用户-->"+userInfo.getUsername()+"获取权限中");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        //用户获取角色集
        List<Role> roleList=userRoleMapper.findByUserName(userInfo.getUsername());
        Set<String> roleSet=new HashSet<>();
        for (Role r:roleList){
            Integer roleId=r.getId();//获取角色id
            simpleAuthorizationInfo.addRole(r.getName());//添加角色名字
            List<Permission> permissionList=rolePermissionMapper.findByRoleId(roleId);
            for (Permission p:permissionList){
                //添加权限
                simpleAuthorizationInfo.addStringPermission(p.getName());
            }
        }

        System.out.println("角色为-> " + simpleAuthorizationInfo.getRoles());
        System.out.println("权限为-> " + simpleAuthorizationInfo.getStringPermissions());
        return simpleAuthorizationInfo;
    }

    /**
     * 登录认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //获取用户输入的用户名密码
        String username= (String) token.getPrincipal();
        String password=new String((char[])token.getCredentials());

        System.out.println("用户输入--->username:"+username+"-->password:"+password);

        //在数据库中查询
        User userInfo=userMapper.selectByName(username);
        if (userInfo == null) {
            throw new UnknownAccountException("用户名或密码错误!");
        }
        if (!password.equals(userInfo.getPassword())) {
            throw new IncorrectCredentialsException("用户名或密码错误!");
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, // 用户名
                userInfo.getPassword(), // 密码
                getName() // realm name
        );
        return authenticationInfo;
    }
}

其中UnknownAccountException等异常为Shiro自带异常,Shiro具有丰富的运行时AuthenticationException层次结构,可以准确指出尝试失败的原因 

创建一个UserController.class类

用于处理User类的访问请求,并使用Shiro权限注解控制权限:

 

/**
 * @ClassName UserController
 * @Description TODO
 * @Author fqCoder
 * @Date 2020/3/3 15:14
 * @Version 1.0
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @RequiresPermissions("user:queryAll")
    @GetMapping("/queryAll")
    public String queryAll(){

        //只演示框架...功能不实现
        return "查询列表";
    }

    @RequiresPermissions("user:add")
    @GetMapping("/add")
    public String userAdd(){
        return "添加用户";
    }

    @RequiresPermissions("user:delete")
    @GetMapping("/delete")
    public String userDelete(){
        return "删除用户";
    }
}

 这只是前后端分离时的shiro验证,不涉及页面的代码

以上就是shiro 整合的操作步骤

三、下面是拓展场景——在项目启动的时候进行全局扫描自动生成权限资源

 假如最开始框架设计时,并没有引入权限框架,中途是安测扫描出越权问题,进行补漏。

这时已经实现了很多接口,设计到加权限问题,如果是注解的方式,一个一个加不太现实,那得累死,这时就可以考虑在项目启动的时候进行全局扫描自动生成权限资源

步骤:

1、定义需要进行权限控制的接口-自动扫描生成的资源注解 

/**
 * 权限注解,用于标识需要权限处理的接口
 *
 * @author
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Auth {
    /**
     * 权限id,模块id + 方法id需要唯一
     */
    long id();

    /**
     * 权限名称
     */
    String name();

    int type() default 1;

    /**
     * 权限菜单路径
     */
    String perssionMenus() default "";
}

 定义组件,项目启动时扫描

/**
 * @author
 */
@Slf4j
@Component
public class ApplicationStartup implements ApplicationRunner {

    @Resource
    private IBackendRabcResourceService resourceService;

    @Override
    public void run(ApplicationArguments args) {
        log.info("--------------->【ApplicationStartup接口初始化】-------> 开始初始化扫描所有需要权限的接口资源");
        resourceService.initRoleResource();
        log.info("--------------->【ApplicationStartup接口初始化完成】------->");

    }


}

service层

    @Override
    public void initRoleResource() {
        List<RabcResource> listAll = new LinkedList<>();
        List<RabcResource> moduleResourceslist = getAuthModuleResources();
        // 扫描并获取所有需要权限处理的接口资源(该方法逻辑写在下面)
        List<RabcResource> methodResourceslist = getAuthMethodResources();
        // 如果权限资源为空,就不用走后续数据插入步骤
        if (CollUtil.isEmpty(moduleResourceslist) && CollUtil.isEmpty(methodResourceslist)) {
            return;
        }
        // 先删除所有操作权限类型的权限资源,待会再新增资源,以实现全量更新(注意哦,数据库中不要设置外键,否则会删除失败)
        if (CollUtil.isNotEmpty(moduleResourceslist)) {
            log.info("--------------->开始删除已存在的模块资源,进行全量更新");
            this.deleteResourceByType(0);
        }
        if (CollUtil.isNotEmpty(methodResourceslist)) {
            log.info("--------------->开始删除已存在的接口资源,进行全量更新");
            this.deleteResourceByType(1);
        }
        listAll.addAll(moduleResourceslist);
        listAll.addAll(methodResourceslist);

        // 将资源数据批量添加到数据库
        this.insertResources(listAll);
        List<RabcRoleResource> roleResources = this.retrieveItemChild();
        if (CollUtil.isNotEmpty(roleResources)) {
            log.info("--------------->开始删除已存在的角色资源,进行全量更新");
            this.deleteRoleResourceByType(0);
        }
        this.insertRoleResource(roleResources);
        log.info("--------------->【ApplicationStartup接口初始化完成】------->");
    }

    /**
     * 扫描并返回所有需要权限处理的模块资源
     */
    private List<RabcResource> getAuthModuleResources() {
        // 接下来要添加到数据库的资源
        List<RabcResource> list = new LinkedList<>();
        // 拿到所有接口信息,并开始遍历
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingInfoHandlerMapping.getHandlerMethods();
        handlerMethods.forEach((info, handlerMethod) -> {
            // 拿到类(模块)上的权限注解
            Auth moduleAuth = handlerMethod.getBeanType().getAnnotation(Auth.class);
            if (moduleAuth == null) {
                return;
            }
            if (0 == moduleAuth.type()) {
                String path = info.getPatternsCondition().getPatterns().toArray()[0].toString();
                String moduleAuthName = "/" + path.split("/")[1];
                RabcResource resource = list.stream().filter(item -> moduleAuthName.equals(item.getPath()))
                        .findFirst()
                        .orElse(null);
                if (ObjectUtil.isEmpty(resource)) {
                    // 将权限名、资源路径、资源类型组装成资源对象,并添加集合中
                    RabcResource resourceEntity = new RabcResource();
                    resourceEntity.setType(0)
                            .setPath(moduleAuthName)
                            .setId(moduleAuth.id())
                            .setPerssionmenus(moduleAuth.perssionMenus())
                            .setName(moduleAuth.name());
                    list.add(resourceEntity);
                }
            }

        });
        log.info("【getAuthModuleResources方法】-------> 获取到所有需要权限控制的模块资源 模块数量:{}", list.size());
        return list;
    }

    /**
     * 扫描并返回所有需要权限处理的接口资源
     */
    private List<RabcResource> getAuthMethodResources() {
        // 接下来要添加到数据库的资源
        List<RabcResource> list = new LinkedList<>();
        // 拿到所有接口信息,并开始遍历
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingInfoHandlerMapping.getHandlerMethods();
        handlerMethods.forEach((info, handlerMethod) -> {
            // 拿到类(模块)上的权限注解
            Auth moduleAuth = handlerMethod.getBeanType().getAnnotation(Auth.class);
            // 拿到接口方法上的权限注解
            Auth methodAuth = handlerMethod.getMethod().getAnnotation(Auth.class);
            // 模块注解和方法注解缺一个都代表不进行权限处理
            if (moduleAuth == null || methodAuth == null) {
                return;
            }

            // 拿到该接口方法的请求方式(GET、POST等)
            Set<RequestMethod> methods = info.getMethodsCondition().getMethods();
            // 如果一个接口方法标记了多个请求方式,权限id是无法识别的,不进行处理
            if (methods.size() != 1) {
                return;
            }
            // 将请求方式和路径用`:`拼接起来,以区分接口。比如:GET:/user/{id}、POST:/user/{id}
            String path = methods.toArray()[0] + ":" + info.getPatternsCondition().getPatterns().toArray()[0];
            String methodPath = info.getPatternsCondition().getPatterns().toArray()[0].toString();
            // 将权限名、资源路径、资源类型组装成资源对象,并添加集合中
           RabcResource resourceEntity = new RabcResource();
            resourceEntity.setType(1)
                    .setPath(path)
                    .setPerssionmenus(moduleAuth.perssionMenus())
                    .setName(methodAuth.name())
                    .setId(moduleAuth.id() + methodAuth.id())
                    .setMethod(methodPath);
            list.add(resourceEntity);
        });
        log.info("【getAuthMethodResources方法】-------> 获取到所有需要权限控制的模块下的具体方法接口资源 模块下方法数量{}", list.size());
        return list;
    }

 

@Auth(id=1000,name = "附件文件上传下载接口", type = 0)
public class AnnexFileController {

    @Autowired
    private AnnexFileService annexFileService;

    @PostMapping("/upload")
    @ApiOperation("文件上传")
    @Auth(id=1,name = "附件文件上传")
    public Result<File> upload(@RequestPart("file") MultipartFile file) throws Exception {
      Member user = LoginUtils.getCurrentMember();
        AmshFile upload = annexFileService.upload(file, user.getUsername());
        return Result.ok(upload);
    }

    @GetMapping("/download")
    @ApiOperation(value = "下载文件")
    @Auth(id=2,name = "附件文件下载")
    public Result<?> download(@ApiParam(value = "文件ID", required = true) String fileId, HttpServletResponse response) {
        annexFileService.downloadFile(fileId, response);
        return Result.ok();
    }

注意只有添加了该注解@Auth(id=2,name = "附件文件下载") ,类型才会进行接口资源扫描,才会加入到权限资源的数据库中,加入到资源数据库中,需要自己修改逻辑。只有同时实现了下面两个注解才会实现控制访问,一个是生成接口权限资源的,一个是接口访问的时候进行权限验证的

 以上是开发过程中遇到问题并解决之后的总结

相关文章:

  • 网站包括什么在线排名优化
  • 网站开发怎么自学seo排名关键词搜索结果
  • 给客户做网站建设方案平面设计
  • 网站前台怎么做数据分析网
  • 青岛靠谱的做网站公司百度扫一扫识别图片在线
  • 直播软件哪个好用seo线下培训课程
  • webscanner漏洞扫描部署使用
  • HTML 表单:构建交互式网页的关键元素
  • 3D 地图渲染-区域纹理图添加
  • 库博静态代码分析工具Jenkins插件集成
  • Husky目标跟踪
  • Spring Boot集成Elasticsearch指南
  • idea清除git密码
  • C++ STL:六大组件全解析
  • 大数据(4.1)Hive架构设计与企业级实战:从内核原理到性能巅峰优化,打造高效数据仓库
  • Qt基本框架(2)
  • 强化学习经典策略梯度算法REINFORCE
  • CMake Presets教程
  • 开发一个小程序需要多久时间?小程序软件开发周期
  • 【Flask开发】嘿马文学web完整flask项目第2篇:2.用户认证,Json Web Token(JWT)【附代码文档】
  • 物联网安全技术:守护智能世界的防线
  • 如何把已有的虚拟环境的python版本进行降级?
  • Java观察者模式详解
  • AI助理是如何助力企业的
  • git克隆数据失败
  • 优维HAO案例:香港联交所上市企业「智能运维平台」项目