Spring Security 动态url权限控制

Spring Security-动态权限控制(1) spring security 动态权限控制 阅读详情

一、前言

本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限

基本环境
  1. spring-boot 2.1.8
  2. mybatis-plus 2.2.0
  3. mysql 数据库
  4. maven项目
Spring Security入门学习可参考之前文章:
  1. SpringBoot集成Spring Security入门体验(一)blog.youkuaiyun.com/qq_38225558…
  2. Spring Security 自定义登录认证(二)blog.youkuaiyun.com/qq_38225558…

二、数据库建表

在这里插入图片描述

表关系简介:
  1. 用户表t_sys_user 关联 角色表t_sys_role 两者建立中间关系表t_sys_user_role
  2. 角色表t_sys_role 关联 权限表t_sys_permission 两者建立中间关系表t_sys_role_permission
  3. 最终体现效果为当前登录用户所具备的角色关联能访问的所有url,只要给角色分配相应的url权限即可

温馨小提示:这里逻辑根据个人业务来定义,小编这里讲解案例只给用户对应的角色分配访问权限,像其它的 直接给用户分配权限等等可以自己实现

表模拟数据如下:

在这里插入图片描述

三、Spring Security 动态权限控制

1、未登录访问权限控制

自定义AdminAuthenticationEntryPoint类实现AuthenticationEntryPoint类

这里是认证权限入口 -> 即在未登录的情况下访问所有接口都会拦截到此(除了放行忽略接口)

温馨小提示:ResponseUtils和ApiResult是小编这里模拟前后端分离情况下返回json格式数据所使用工具类,具体实现可参考文末给出的demo源码

@Component
public class AdminAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
        ResponseUtils.out(response, ApiResult.fail("未登录!!!"));
    }
}复制代码
2、自定义过滤器MyAuthenticationFilter继承OncePerRequestFilter实现访问鉴权

每次访问接口都会经过此,我们可以在这里记录请求参数、响应内容,或者处理前后端分离情况下,以token换用户权限信息,token是否过期,请求头类型是否正确,防止非法请求等等

  1. logRequestBody()方法:记录请求消息体
  2. logResponseBody()方法:记录响应消息体

【注:请求的HttpServletRequest流只能读一次,下一次就不能读取了,因此这里要使用自定义的MultiReadHttpServletRequest工具解决流只能读一次的问题,响应同理,具体可参考文末demo源码实现】

@Slf4j
@Component
public class MyAuthenticationFilter extends OncePerRequestFilter {

    private final UserDetailsServiceImpl userDetailsService;

    protected MyAuthenticationFilter(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("请求头类型: " + request.getContentType());
        if ((request.getContentType() == null && request.getContentLength() > 0) || (request.getContentType() != null && !request.getContentType().contains(Constants.REQUEST_HEADERS_CONTENT_TYPE))) {
            filterChain.doFilter(request, response);
            return;
        }

        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
        MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(response);
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            // 记录请求的消息体
            logRequestBody(wrappedRequest);

//            String token = "123";
            // 前后端分离情况下,前端登录后将token储存在cookie中,每次访问接口时通过token去拿用户权限
            String token = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
            log.debug("后台检查令牌:{}", token);
            if (StringUtils.isNotBlank(token)) {
                // 检查token
                SecurityUser securityUser = userDetailsService.getUserByToken(token);
                if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
                    throw new AccessDeniedException("TOKEN已过期,请重新登录!");
                }
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
                // 全局注入角色权限信息和登录用户基本信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } finally {
            stopWatch.stop();
            long usedTimes = stopWatch.getTotalTimeMillis();
            // 记录响应的消息体
            logResponseBody(wrappedRequest, wrappedResponse, usedTimes);
        }

    }

    private String logRequestBody(MultiReadHttpServletRequest request) {
        MultiReadHttpServletRequest wrapper = request;
        if (wrapper != null) {
            try {
                String bodyJson = wrapper.getBodyJsonStrByJson(request);
                String url = wrapper.getRequestURI().replace("//", "/");
                System.out.println("-------------------------------- 请求url: " + url + " --------------------------------");
                Constants.URL_MAPPING_MAP.put(url, url);
                log.info("`{}` 接收到的参数: {}",url , bodyJson);
                return bodyJson;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void logResponseBody(MultiReadHttpServletRequest request, MultiReadHttpServletResponse response, long useTime) {
        MultiReadHttpServletResponse wrapper = response;
        if (wrapper != null) {
            byte[] buf = wrapper.getBody();
            if (buf.length > 0) {
                String payload;
                try {
                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                } catch (UnsupportedEncodingException ex) {
                    payload = "[unknown]";
                }
                log.info("`{}`  耗时:{}ms  返回的参数: {}", Constants.URL_MAPPING_MAP.get(request.getRequestURI()), useTime, payload);
            }
        }
    }

}复制代码
3、自定义UserDetailsServiceImpl实现UserDetailsService 和 自定义SecurityUser实现UserDetails 认证用户详情

这个在上一篇文章中也提及过,但上次未做角色权限处理,这次我们来一起加上吧

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;

    /***
     * 根据账号获取用户信息
     * @param username:
     * @return: org.springframework.security.core.userdetails.UserDetails
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从数据库中取出用户信息
        List<User> userList = userMapper.selectList(new EntityWrapper<User>().eq("username", username));
        User user;
        // 判断用户是否存在
        if (!CollectionUtils.isEmpty(userList)) {
            user = userList.get(0);
        } else {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        // 返回UserDetails实现类
        return new SecurityUser(user, getUserRoles(user.getId()));
    }

    /***
     * 根据token获取用户权限与基本信息
     *
     * @param token:
     * @return: com.zhengqing.config.security.dto.SecurityUser
     */
    public SecurityUser getUserByToken(String token) {
        User user = null;
        List<User> loginList = userMapper.selectList(new EntityWrapper<User>().eq("token", token));
        if (!CollectionUtils.isEmpty(loginList)) {
            user = loginList.get(0);
        }
        return user != null ? new SecurityUser(user, getUserRoles(user.getId())) : null;
    }

    /**
     * 根据用户id获取角色权限信息
     *
     * @param userId
     * @return
     */
    private List<Role> getUserRoles(Integer userId) {
        List<UserRole> userRoles = userRoleMapper.selectList(new EntityWrapper<UserRole>().eq("user_id", userId));
        List<Role> roleList = new LinkedList<>();
        for (UserRole userRole : userRoles) {
            Role role = roleMapper.selectById(userRole.getRoleId());
            roleList.add(role);
        }
        return roleList;
    }

}复制代码

这里再说下自定义SecurityUser 是因为Spring Security自带的 UserDetails (存储当前用户基本信息) 有时候可能不满足我们的需求,因此我们可以自己定义一个来扩展我们的需求在这里插入图片描述getAuthorities()方法:即授予当前用户角色权限信息

@Data
@Slf4j
public class SecurityUser implements UserDetails {
    /**
     * 当前登录用户
     */
    private transient User currentUserInfo;
    /**
     * 角色
     */
    private transient List<Role> roleList;

    public SecurityUser() { }

    public SecurityUser(User user) {
        if (user != null) {
            this.currentUserInfo = user;
        }
    }

    public SecurityUser(User user, List<Role> roleList) {
        if (user != null) {
            this.currentUserInfo = user;
            this.roleList = roleList;
        }
    }

    /**
     * 获取当前用户所具有的角色
     *
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(this.roleList)) {
            for (Role role : this.roleList) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getCode());
                authorities.add(authority);
            }
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return currentUserInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return currentUserInfo.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}复制代码
4、自定义UrlFilterInvocationSecurityMetadataSource实现FilterInvocationSecurityMetadataSource重写getAttributes()方法 获取访问该url所需要的角色权限信息

执行完之后到 下一步 UrlAccessDecisionManager 中认证权限

@Component
public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Autowired
    PermissionMapper permissionMapper;
    @Autowired
    RolePermissionMapper rolePermissionMapper;
    @Autowired
    RoleMapper roleMapper;

    /***
     * 返回该url所需要的用户权限信息
     *
     * @param object: 储存请求url信息
     * @return: null:标识不需要任何权限都可以访问
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 获取当前请求url
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        // TODO 忽略url请放在此处进行过滤放行
        if ("/login".equals(requestUrl) || requestUrl.contains("logout")) {
            return null;
        }

        // 数据库中所有url
        List<Permission> permissionList = permissionMapper.selectList(null);
        for (Permission permission : permissionList) {
            // 获取该url所对应的权限
            if (requestUrl.equals(permission.getUrl())) {
                List<RoleMenu> permissions = rolePermissionMapper.selectList(new EntityWrapper<RoleMenu>().eq("permission_id", permission.getId()));
                List<String> roles = new LinkedList<>();
                if (!CollectionUtils.isEmpty(permissions)){
                    Integer roleId = permissions.get(0).getRoleId();
                    Role role = roleMapper.selectById(roleId);
                    roles.add(role.getCode());
                }
                // 保存该url对应角色权限信息
                return SecurityConfig.createList(roles.toArray(new String[roles.size()]));
            }
        }
        // 如果数据中没有找到相应url资源则为非法访问,要求用户登录再进行操作
        return SecurityConfig.createList(Constants.ROLE_LOGIN);
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return FilterInvocation.class.isAssignableFrom(aClass);
    }
}复制代码
5、自定义UrlAccessDecisionManager实现AccessDecisionManager重写decide()方法 对访问url进行权限认证处理

此处小编的处理逻辑是只要包含其中一个角色即可访问

@Component
public class UrlAccessDecisionManager implements AccessDecisionManager {

    /**
     * @param authentication: 当前登录用户的角色信息
     * @param object: 请求url信息
     * @param collection: `UrlFilterInvocationSecurityMetadataSource`中的getAttributes方法传来的,表示当前请求需要的角色(可能有多个)
     * @return: void
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, AuthenticationException {
        // 遍历角色
        for (ConfigAttribute ca : collection) {
            // ① 当前url请求需要的权限
            String needRole = ca.getAttribute();
            if (Constants.ROLE_LOGIN.equals(needRole)) {
                if (authentication instanceof AnonymousAuthenticationToken) {
                    throw new BadCredentialsException("未登录!");
                } else {
                    throw new AccessDeniedException("未授权该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 true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}复制代码
6、自定义无权限处理器 UrlAccessDeniedHandler实现AccessDeniedHandler重写handle()方法

在这里自定义403无权限响应内容,登录过后的权限处理【 注:要和未登录时的权限处理区分开哦~ 】

@Component
public class UrlAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        ResponseUtils.out(response, ApiResult.fail(403, e.getMessage()));
    }
}复制代码
7、最后在Security 核心配置类中配置以上处理
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 访问鉴权 - 认证token、签名...
     */
    private final MyAuthenticationFilter myAuthenticationFilter;
    /**
     * 访问权限认证异常处理
     */
    private final AdminAuthenticationEntryPoint adminAuthenticationEntryPoint;
    /**
     * 用户密码校验过滤器
     */
    private final AdminAuthenticationProcessingFilter adminAuthenticationProcessingFilter;

    // 上面是登录认证相关  下面为url权限相关 - ========================================================================================

    /**
     * 获取访问url所需要的角色信息
     */
    private final UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;
    /**
     * 认证权限处理 - 将上面所获得角色权限与当前登录用户的角色做对比,如果包含其中一个角色即可正常访问
     */
    private final UrlAccessDecisionManager urlAccessDecisionManager;
    /**
     * 自定义访问无权限接口时403响应内容
     */
    private final UrlAccessDeniedHandler urlAccessDeniedHandler;

    public SecurityConfig(MyAuthenticationFilter myAuthenticationFilter, AdminAuthenticationEntryPoint adminAuthenticationEntryPoint, AdminAuthenticationProcessingFilter adminAuthenticationProcessingFilter, UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource, UrlAccessDeniedHandler urlAccessDeniedHandler, UrlAccessDecisionManager urlAccessDecisionManager) {
        this.myAuthenticationFilter = myAuthenticationFilter;
        this.adminAuthenticationEntryPoint = adminAuthenticationEntryPoint;
        this.adminAuthenticationProcessingFilter = adminAuthenticationProcessingFilter;
        this.urlFilterInvocationSecurityMetadataSource = urlFilterInvocationSecurityMetadataSource;
        this.urlAccessDeniedHandler = urlAccessDeniedHandler;
        this.urlAccessDecisionManager = urlAccessDecisionManager;
    }


    /**
     * 权限配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();

        // 禁用CSRF 开启跨域
        http.csrf().disable().cors();

        // 未登录认证异常
        http.exceptionHandling().authenticationEntryPoint(adminAuthenticationEntryPoint);
        // 登录过后访问无权限的接口时自定义403响应内容
        http.exceptionHandling().accessDeniedHandler(urlAccessDeniedHandler);

        // url权限认证处理
        registry.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                o.setAccessDecisionManager(urlAccessDecisionManager);
                return o;
            }
        });

        // 不创建会话 - 即通过前端传token到后台过滤器中验证是否存在访问权限
//        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 标识访问 `/home` 这个接口,需要具备`ADMIN`角色
//        registry.antMatchers("/home").hasRole("ADMIN");
        // 标识只能在 服务器本地ip[127.0.0.1或localhost] 访问 `/home` 这个接口,其他ip地址无法访问
        registry.antMatchers("/home").hasIpAddress("127.0.0.1");
        // 允许匿名的url - 可理解为放行接口 - 多个接口使用,分割
        registry.antMatchers("/login", "/index").permitAll();
//        registry.antMatchers("/**").access("hasAuthority('admin')");
        // OPTIONS(选项):查找适用于一个特定网址资源的通讯选择。 在不需执行具体的涉及数据传输的动作情况下, 允许客户端来确定与资源相关的选项以及 / 或者要求, 或是一个服务器的性能
        registry.antMatchers(HttpMethod.OPTIONS, "/**").denyAll();
        // 自动登录 - cookie储存方式
        registry.and().rememberMe();
        // 其余所有请求都需要认证
        registry.anyRequest().authenticated();
        // 防止iframe 造成跨域
        registry.and().headers().frameOptions().disable();

        // 自定义过滤器在登录时认证用户名、密码
        http.addFilterAt(adminAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(myAuthenticationFilter, BasicAuthenticationFilter.class);
    }

    /**
     * 忽略拦截url或静态资源文件夹 - web.ignoring(): 会直接过滤该url - 将不会经过Spring Security过滤器链
     *                             http.permitAll(): 不会绕开springsecurity验证,相当于是允许该路径通过
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.GET,
                "/favicon.ico",
                "/*.html",
                "/**/*.css",
                "/**/*.js");
    }

}复制代码

四、编写测试代码

控制层:

@Slf4j
@RestController
public class IndexController {

    @GetMapping("/")
    public ModelAndView showHome() {
        return new ModelAndView("home.html");
    }

    @GetMapping("/index")
    public String index() {
        return "Hello World ~";
    }

    @GetMapping("/login")
    public ModelAndView login() {
        return new ModelAndView("login.html");
    }

    @GetMapping("/home")
    public String home() {
        String name = SecurityContextHolder.getContext().getAuthentication().getName();
        log.info("登陆人:" + name);
        return "Hello~ " + name;
    }

    @GetMapping(value ="/admin")
    // 访问路径`/admin` 具有`ADMIN`角色权限   【这种是写死方式】
//    @PreAuthorize("hasPermission('/admin','ADMIN')")
    public String admin() {
        return "Hello~ 管理员";
    }

    @GetMapping("/test")
    public String test() {
        return "Hello~ 测试权限访问接口";
    }
    
}复制代码

页面和其它相关代码这里就不贴出来了,具体可参考文末demo源码

五、运行访问测试效果

1、未登录时

在这里插入图片描述

2、登录过后如果有权限则正常访问

在这里插入图片描述

3、登录过后,没有权限

这里我们可以修改数据库角色权限关联表t_sys_role_permission来进行测试哦 ~

Security 动态url权限也就是依赖这张表来判断的,只要修改这张表分配角色对应url权限资源,用户访问url时就会动态的去判断,无需做其他处理,如果是将权限信息放在了缓存中,修改表数据时及时更新缓存即可!在这里插入图片描述

在这里插入图片描述

4、登录过后,访问数据库中没有配置的url 并且 在Security中没有忽略拦截的url时

在这里插入图片描述

六、总结

  1. 自定义未登录权限处理器AdminAuthenticationEntryPoint - 自定义未登录时访问无权限url响应内容
  2. 自定义访问鉴权过滤器MyAuthenticationFilter - 记录请求响应日志、是否合法访问,验证token过期等
  3. 自定义UrlFilterInvocationSecurityMetadataSource - 获取访问该url所需要的角色权限
  4. 自定义UrlAccessDecisionManager - 对访问url进行权限认证处理
  5. 自定义UrlAccessDeniedHandler - 登录过后访问无权限url失败处理器 - 自定义403无权限响应内容
  6. 在Security核心配置类中配置以上处理器和过滤器
Security动态权限相关代码:

在这里插入图片描述

本文案例demo源码

gitee.com/zhengqingya…

来源:https://juejin.cn/post/6844903970159198216#heading-21

Spring Boot+Mybatis Plus +Spring Security 动态url权限控制(三) 准备开Spring Security 权限工作 三.创建好Spring Security相应的工具类 Constants:全局变量工具类 jasksonAnnotationlntrospectorEx:扩展JACKSON对FastJson注释JSONField的支持工具类 MultiReadHttpServletRequest:多次读写BODY用HTTP REQUES... 阅读详情

相关推荐

Spring Boot+Mybatis Plus +Spring Security 动态url权限控制(二)

一,准备工作 1.1 导入相依的jar包pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ...

weixin_45863786的博客 927

SpringBoot整合SpringSecurity权限控制(动态拦截url+单点登录)

Slf4j@Component@Resource@Override//获取身份验证详细信息//用于校验mac地址白名单(这里只是打个比方,登录验证中增加的额外字段)//表单输入的用户名//表单输入的密码//校验用户名密码if (!matches) {!");@Override@Component@Resource@Override//任意登录用户名!");//从数据库获取密码//用户拥有的角色// }

jiaoqi6132的博客 2629

Spring Boot+Spring Security:基于URL动态权限:自定义Filter - 第17篇

需求缘起 在上一篇我们通过自定义AccessDesionManager实现了动态权限控制,本节将通过自定义Filter进行实现动态权限,理解了上一篇文章的话,对于这篇文章就没有什么难点了。代码基于《基于URL动态权限:准备工作》在往下编码。 编码思路 对于URL动态权限配置,主要解决如下几个问题: (1)基于URL的用户权限信息保存在哪里:需要定义一张权限表,...

悟纤学院 2万+

Spring Security- 动态 URL 权限控制的实现思路

Spring Security 动态 URL 权限控制实现 本文介绍了一种基于 Spring Security 的动态 URL 权限控制方案,主要解决传统静态权限配置不灵活的问题。文章包含以下核心内容: 需求背景:传统注解或配置方式无法满足运行时动态调整权限的需求,特别是在多租户、复杂业务场景下。 技术方案: 采用"拦截器+权限决策器"架构 通过自定义 SecurityMetadataSource 从数据库加载权限规则 使用 AccessDecisionManager 进行实时权限验证 实现步骤: 设计 R

千淘万漉虽辛苦,吹尽狂沙始到金 1万+

spring security动态配置url权限的2种实现方法

对于使用spring security来说,存在一种需求,就是动态去配置url的权限,即在运行时去配置url对应的访问角色。下面这篇文章主要给大家介绍了关于spring security动态配置url权限的2种实现方法,需要的朋友可以参考下

Spring Security如何使用URL地址进行权限控制

主要介绍了Spring Security如何使用URL地址进行权限控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

websecurity连接mysql_Spring Security 动态url权限控制(三)

一、前言本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限基本环境spring-boot 2.1.8mybatis-plus 2.2.0mysql 数据库maven项目Spring Security入门学习可参考之前文章:二、数据库建表表关系简介:用户表t_sys_user 关联 角色表t_sys_role 两者建立中间关系...

weixin_32050773的博客 353

别再让信号‘撞墙’了!手把手教你用嘉立创阻抗计算器搞定PCB高速线(附免费压层结构选择)

本文详细介绍了如何使用嘉立创阻抗计算器解决PCB高速线设计中的阻抗匹配问题,避免信号反射和传输损耗。通过免费压层结构选择和五步计算法,帮助工程师快速掌握USB3.0差分线等高速信号的设计技巧,提升PCB设计效率和信号质量。

weixin_30736301的博客 282

Spring Security(3) 动态url权限控制

一、前言 本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限 基本环境 spring-boot 2.1.8 mybatis-plus 2.2.0 mysql 数据库 maven项目 Spring Security入门学习可参考之前文章: SpringBoot集成Spring Security入门体验(一) https:...

郑清 1万+

LTspice导入德州仪器SPICE模型的实用指南与常见问题解析

本文详细介绍了如何在LTspice中导入德州仪器的SPICE模型,包括模型文件获取、路径管理、导入步骤及常见问题解析。通过实用指南帮助工程师精确仿真电路性能,提升设计效率,重点涵盖了第三方模型的导入教程和最佳实践。

kk1234的博客 314

深度学习12—VGG19实现

state_dict = load_state_dict_from_url(model_urls['vgg19'],model_dir='./model' ,progress=progress)#预训练模型地址。accuracy=((out.argmax(1)==label).sum()).clone().detach().cpu().numpy()#正确预测的总和比测试集的长度,即预测正确的精度。image=torch.reshape(image,(1,3,224,224))#四维图形,RGB三个通。

m0_55196097的博客 2444

Spring Security 动态url权限控制(三)

一、前言 本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限 基本环境 spring-boot 2.1.8 mybatis-plus 2.2.0 mysql 数据库 maven项目 Spring Security入门学习可参考之前文章: SpringBoot集成Spring Security入门体验(一...

songfei_dream的专栏 1288

Spirent TestCenter 3.90吞吐测试全流程详解(附RFC 2544配置避坑指南)

本文详细解析了使用Spirent TestCenter 3.90进行网络吞吐测试的全流程,并提供了符合RFC 2544标准的配置指南与避坑要点。内容涵盖测试环境搭建、核心概念澄清、STC软件配置步骤、结果深度解读以及自动化脚本入门,旨在帮助网络测试工程师精准评估设备性能,规避常见配置陷阱。

weixin_42602368的博客 370

java 权限url权限_Spring Security 动态url权限控制(三)

一、前言本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限基本环境spring-boot 2.1.8mybatis-plus 2.2.0mysql 数据库maven项目Spring Security入门学习可参考之前文章:SpringBoot集成Spring Security入门体验(一)Spring Security 自定...

weixin_39878989的博客 313

ETAS工具导入DBC生成Com协议栈

ETAS工具导入DBC主要也是生成arxml用的,ETAS推荐使用Cobra导入,本文介绍导入过程及注意事项不管是导入DBC还是arxml,本质上都是为了生成对应的com协议栈,目的是为了减少重复的工作量,所以,前期定义好收发的矩阵很重要,一般导入一次之后,后面就可以按变更点手动更改了。

赞的博客 1714

【Unity-UGUI控件全面解析】| Layout自动布局组件详解

UGUI中有个自动布局组件Auto Layout,主要包含下面几个模块(点击可进入官方文档下查看描述): 自动布局 布局元素 (Layout Element) 水平布局组 (Horizontal Layout Group) 垂直布局组 (Vertical Layout Group) 网格布局组 (Grid Layout Group) 内容大小适配器 (Content Size Fitter) 自动布局系统 基于布局元素和布局控制器的概念。一个布局元素是具有矩形变换以及其他可选组件的游戏对象。布局元素对自身

努力前行,总会成为自己心中的那道光 2万+

动态参数 maven_Spring Security 动态url权限控制(三)

一、前言本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限基本环境spring-boot 2.1.8mybatis-plus 2.2.0mysql 数据库maven项目Spring Security入门学习可参考之前文章:SpringBoot集成Spring Security入门体验(一)Spring Security 自定...

weixin_30937427的博客 334

超实用Kimi越狱提示词!AI红队实战研究、无损保留原生功能、仅突破模型安全限制边界

超实用Kimi越狱提示词!AI红队实战研究、无损保留原生功能、仅突破模型安全限制边界

Chengbei11的博客 1744

循序渐进学spring security 第十一篇 如何动态权限控制URL?如何动态给用户添加权限?

文章目录回顾为什么要动态权限?如何更好的管理权限?数据库设计用户表定义角色表定义菜单表角色人员表角色菜单表如何实现动态权限管理?动态获取url权限配置权限判断搭建项目创建项目:security-mybatis-jwt-perm添加maven依赖:修改pom项目名和artifactId 都统一为:security-mybatis-jwt-perm新建数据库操作相关类和mapper创建实体类添加mybatis 的mapper.xml添加mapper接口动态权限配置动态获取url权限配置动态权限判断配置Secur

huangxuanheng的专栏 5454

为什么 ChatGPT 生成的下载地址会长成 sky-metal-cards-jerry.quirkypony34.chatgpt.site 这种奇怪的样子

我今天看到的这条地址,确实很容易让人产生一种感觉,ChatGPT 明明只是生成了几张 PNG 图片和一个压缩包,为什么最终拿到的却不是类似这种看起来很正规的下载链接,而是一长串带有、UUID、apidecks的地址。更容易让人疑惑的是,其中还出现了一个平时很少见到的域名。这并不是浏览器随机生成的临时 URL,也不是某个来历不明的第三方文件服务器。根据 OpenAI 在 2026 年公布的 ChatGPT Sites 文档和相关服务条款,

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 66

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Spring Boot+Mybatis Plus +Spring Security 动态url权限控制(一)

登录认证 整个权限详细配置

weixin_45863786的博客 611

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: 3种 Springboot 全局时间格式化方式,别再写重复代码了
下一篇: Git 笔记 - 程序员都要掌握的 Git
码趣资源网
博客等级 码龄4年 269粉丝 518原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值