likes
comments
collection
share

Spring Security入门教程:实现用户的授权功能

作者站长头像
站长
· 阅读数 4

上一篇文章我们讲了怎么去实现自定义用户配置,其实大家学习spring security的时候,他其实做的只有两件事,第一是认证,第二是授权。那么前面我们所学的两节课就是认证。但是他登陆之后,我们需要给定指定的资源去给他访问。给哪些资源?这就是授权。所以说我们这节课来学习一下。Spring Security的授权。

首先我们来进行一个简单的演示。新建一个用于演示的接口。

package com.masiyi.springsecuritydemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/admin")  //ADMIN
    public String admin() {
        return "admin ok";
    }

    @GetMapping("/user")  //USER
    public String user() {
        return "user ok";
    }

    @GetMapping("/getInfo")  //READ_INFO
    public String getInfo() {
        return "info ok";
    }

}

接着我们在配置类里面进行如下配置

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN","USER").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("lisi").password("{noop}123").roles("USER").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("win7").password("{noop}123").authorities("READ_INFO").build());
        return inMemoryUserDetailsManager;
    }

如果登陆admin用户成功了,并且访问这个地址就会发现成功访问。

Spring Security入门教程:实现用户的授权功能

如果我们登录角色不是admin的用户就会显示这样的页面。返回403状态码。

Spring Security入门教程:实现用户的授权功能

但是我们刚刚编写的代码,它其实是在内存中创建对应的用户。但是实际的项目中我们肯定不会这么用的,我们肯定也会跟用户存储一样。把授权菜单角色给放入到数据库中。下一步我们去实现这个步骤。把这些放入数据库中去实现。

这里给大家准备了一个SQL脚本,大家直接执行就行。这个里面我们创建了菜单,角色,用户,还有他们之间的关系表并且给他们初始化数据。

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50738
 Source Host           : localhost:3306
 Source Schema         : springsecurity

 Target Server Type    : MySQL
 Target Server Version : 50738
 File Encoding         : 65001

 Date: 22/02/2024 21:47:51
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pattern` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '/admin/**');
INSERT INTO `menu` VALUES (2, '/user/**');
INSERT INTO `menu` VALUES (3, '/guest/**');

-- ----------------------------
-- Table structure for menu_role
-- ----------------------------
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `mid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `mid`(`mid`) USING BTREE,
  INDEX `rid`(`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of menu_role
-- ----------------------------
INSERT INTO `menu_role` VALUES (1, 1, 1);
INSERT INTO `menu_role` VALUES (2, 2, 2);
INSERT INTO `menu_role` VALUES (3, 3, 3);
INSERT INTO `menu_role` VALUES (4, 3, 2);

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `name_zh` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_ADMIN', '系统管理员');
INSERT INTO `role` VALUES (2, 'ROLE_USER', '普通用户');
INSERT INTO `role` VALUES (3, 'ROLE_GUEST', '游客');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT 1,
  `accountNonExpired` tinyint(1) DEFAULT 1,
  `accountNonLocked` tinyint(1) DEFAULT 1,
  `credentialsNonExpired` tinyint(1) DEFAULT 1,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin', '{noop}123', 1, 1, 1, 1);
INSERT INTO `user` VALUES (2, 'user', '{noop}123', 1, 1, 1, 1);
INSERT INTO `user` VALUES (3, 'masiyi', '{noop}123', 1, 1, 1, 1);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `uid`(`uid`) USING BTREE,
  INDEX `rid`(`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);

SET FOREIGN_KEY_CHECKS = 1;

创建表对应的实体类。

package com.masiyi.springsecuritydemo.entity;

import java.util.List;

public class Menu {
    private Integer id;
    private String pattern;
    private List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
}
package com.masiyi.springsecuritydemo.entity;

import java.io.Serializable;

public class Role implements Serializable {
    private Integer id;
    private String name;
    private String nameZh;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }
}
/*
 * Copyright 2013-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.masiyi.springsecuritydemo.entity;

public class User {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public User(Integer age, String name) {
        this.name = name;
        this.age = age;
    }

    public User() {
    }
}

这里为了区分数据库对应的实体类跟spring security里面的userdetails,所以做了两个类去区分。

package com.masiyi.springsecuritydemo.entity;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

public class UserDetail implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean accountNonExpired;
    private Boolean accountNonLocked;
    private Boolean credentialsNonExpired;
    private List<Role> roles = new ArrayList<>();

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserDetail userDetail = (UserDetail) o;
        return Objects.equals(id, userDetail.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        roles.forEach(role -> grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())));
        return grantedAuthorities;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public String getPassword() {
        return password;
    }


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

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

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

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setUsername(String username) {
        this.username = username;
    }


    public void setPassword(String password) {
        this.password = password;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public Boolean getAccountNonExpired() {
        return accountNonExpired;
    }

    public void setAccountNonExpired(Boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

    public Boolean getAccountNonLocked() {
        return accountNonLocked;
    }

    public void setAccountNonLocked(Boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    public Boolean getCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

创建对应的xml

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.masiyi.springsecuritydemo.dao.MenuDao">


    <resultMap id="MenuResultMap" type="com.masiyi.springsecuritydemo.entity.Menu">
        <id property="id" column="id"/>
        <result property="pattern" column="pattern"/>
        <collection property="roles" ofType="com.masiyi.springsecuritydemo.entity.Role">
            <id column="rid" property="id"/>
            <result column="rname" property="name"/>
            <result column="rnameZh" property="nameZh"/>
        </collection>
    </resultMap>

    <select id="getAllMenu" resultMap="MenuResultMap">
        select m.*, r.id as rid, r.name as rname, r.name_zh as rnameZh
        from menu m
        left join menu_role mr on m.`id` = mr.`mid`
        left join role r on r.`id` = mr.`rid`
    </select>
</mapper>
<?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.masiyi.springsecuritydemo.dao.UserDao">
    <insert id="insertUser">
        insert into user(username,password) values (#{userInfo.username},#{userInfo.password})
    </insert>
    <!--查询单个-->
    <select id="loadUserByUsername" resultType="com.masiyi.springsecuritydemo.entity.UserDetail">
        select id,
               username,
               password,
               enabled,
               accountNonExpired,
               accountNonLocked,
               credentialsNonExpired
        from user
        where username = #{username}
    </select>

    <!--查询指定行数据-->
    <select id="getRolesByUid" resultType="Role">
        select r.id,
               r.name,
               r.name_zh nameZh
        from role r,
             user_role ur
        where r.id = ur.rid
          and ur.uid = #{uid}
    </select>

    <select id="getUserRoleByUid" resultType="com.masiyi.springsecuritydemo.entity.Role">
        select r.*
        from role r,
             user_role ur
        where ur.uid = #{uid}
          and ur.rid = r.id
    </select>
</mapper>

由于篇幅原因,这里就不把全部的代码给贴出来了。大家可以到我的代码仓库里面,把一些剩余的代码给拷贝过来。之后我们把配置类改写成这样。

package com.masiyi.springsecuritydemo.config;

import com.masiyi.springsecuritydemo.handler.MyAuthenticationFailureHandler;
import com.masiyi.springsecuritydemo.handler.MyAuthenticationSuccessHandler;
import com.masiyi.springsecuritydemo.handler.MyLogoutSuccessHandler;
import com.masiyi.springsecuritydemo.metasource.CustomerSecurityMetaSource;
import com.masiyi.springsecuritydemo.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true,securedEnabled=true, jsr250Enabled=true)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {


    private FindByIndexNameSessionRepository sessionRepository;
    private CustomerSecurityMetaSource customSecurityMetadataSource;

    @Autowired
    @Lazy
    public void SecurityConfig(FindByIndexNameSessionRepository sessionRepository,
                               CustomerSecurityMetaSource customSecurityMetadataSource) {
        this.sessionRepository = sessionRepository;
        this.customSecurityMetadataSource = customSecurityMetadataSource;

    }

    /**
     * 这里有两种方式 authorizeHttpRequests 和 authorizeRequests
     *
     * @param http the {@link HttpSecurity} to modify
     * @throws Exception
     */
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.authorizeHttpRequests()
//                .mvcMatchers("/user/register").permitAll()
//                .mvcMatchers(HttpMethod.GET,"/admin").hasRole("ADMIN")  //具有 admin 角色   强大 通用: /admin /admin/  /admin.html
//                .mvcMatchers("/user").hasRole("USER")    //具有 user 角色
//                .mvcMatchers("/getInfo").hasAuthority("READ_INFO") //READ_INFO 权限
//                .antMatchers(HttpMethod.GET,"/admin").hasRole("ADMIN")
//                .anyRequest().authenticated()
//                .and().formLogin()
//                .successHandler(new MyAuthenticationSuccessHandler())
//                .failureHandler(new MyAuthenticationFailureHandler())
//                .and()
//                .logout().logoutSuccessHandler(new MyLogoutSuccessHandler())
//                .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
//                .and()
//                .cors() //跨域处理方案
//                .configurationSource(configurationSource())
//                .and()
//                .rememberMe() //开启记住我功能
//                .and()
//                .sessionManagement()  //开启会话管理
//                .maximumSessions(1)  //设置会话并发数为 1 在SessionRegistryImpl中管理key,key存的是对象,必须要重写User的equals和hashcode方法才能判断用户相等
//        ;
//    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //1.获取工厂对象
        ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
        //2.设置自定义 url 权限处理
        http.apply(new UrlAuthorizationConfigurer<>(applicationContext))
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(customSecurityMetadataSource);
                        //是否拒绝公共资源访问
                        object.setRejectPublicInvocations(false);
                        return object;
                    }
                });
        http.formLogin().and().csrf().disable();
    }

    /**
     *允许所有来源的请求(*)使用任意的请求头和请求方法,并且设置了最大缓存时间为 3600 秒。
     * @return
     */
    CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
//
//    @Bean
//    public UserDetailsService userDetailsService() {
//        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN","USER").build());
//        inMemoryUserDetailsManager.createUser(User.withUsername("lisi").password("{noop}123").roles("USER").build());
//        inMemoryUserDetailsManager.createUser(User.withUsername("win7").password("{noop}123").authorities("READ_INFO").build());
//        return inMemoryUserDetailsManager;
//    }




    @Bean
    public SpringSessionBackedSessionRegistry sessionRegistry() {
        return new SpringSessionBackedSessionRegistry(sessionRepository);
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new MyUserDetailService();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
        auth.userDetailsService(userDetailsService());

    }
//
//    /**
//     * 指定加密方式
//     */
//    @Bean
//    public PasswordEncoder passwordEncoder() {
//        // 使用BCrypt加密密码
//        return new BCryptPasswordEncoder();
//    }
}

其中最重要的部分就是这一块的改变。通过这样写,我们就可以动态的获取数据库里面的信息。

Spring Security入门教程:实现用户的授权功能

这段代码是一个用于配置Spring Security的configure(HttpSecurity http)方法。在该方法中,首先通过http.getSharedObject(ApplicationContext.class)获取ApplicationContext对象,然后通过http.apply(new UrlAuthorizationConfigurer<>(applicationContext))设置自定义的URL权限处理。

在ObjectPostProcessor中,通过object.setSecurityMetadataSource(customSecurityMetadataSource)设置了自定义的安全元数据源customSecurityMetadataSource,用于动态获取资源的权限配置信息。另外,通过object.setRejectPublicInvocations(false)设置是否拒绝公共资源访问为false,允许公共资源的访问。好

最后,通过http.formLogin().and().csrf().disable()配置了表单登录和禁用CSRF保护。

这个里面我们新建了一个CustomerSecurityMetaSource 类。

package com.masiyi.springsecuritydemo.metasource;

import com.masiyi.springsecuritydemo.entity.Menu;
import com.masiyi.springsecuritydemo.entity.Role;
import com.masiyi.springsecuritydemo.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;

@Component
public class CustomerSecurityMetaSource implements FilterInvocationSecurityMetadataSource {

    private final MenuService menuService;

    @Autowired
    public CustomerSecurityMetaSource(MenuService menuService) {
        this.menuService = menuService;
    }

    AntPathMatcher antPathMatcher = new AntPathMatcher();
    /**
     * 自定义动态资源权限元数据信息
     * @param object the object being secured
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //1.当前请求对象
        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();
        //2.查询所有菜单
        List<Menu> allMenu = menuService.getAllMenu();

        for (Menu menu : allMenu) {
            if (antPathMatcher.match(menu.getPattern(), requestURI)) {
                String[] roles = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
                return SecurityConfig.createList(roles);
            }
        }
        return null;
    }

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

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

这段代码是一个自定义的Spring Security权限元数据源(SecurityMetadataSource),用于动态获取资源的权限配置信息。在该类中,通过实现FilterInvocationSecurityMetadataSource接口,重写了getAttributes()方法,根据请求的URL动态获取对应的权限配置信息。

具体来说,该类通过注入MenuService来获取所有菜单信息,然后根据请求的URL匹配菜单的URL模式,从而确定该请求需要的权限配置。如果请求的URL匹配到了某个菜单的URL模式,就返回该菜单所需的角色权限;否则返回null。

通过这种方式,可以实现动态的资源权限配置,根据不同的URL动态确定需要的权限,从而实现细粒度的权限控制。

这样的话,一个实现用户的授权功能的基本框架就搭出来了。至于后面的菜单角色跟用户的真相改查。这里就不一一做描述。相信这些功能已经很简单了。大家可以根据项目中实际的情况进行增删改查功能的编写。至此Spring Security的入门教程这篇专栏就到这里就结束了,相信大家读了这几篇专栏之后,会对spring security有一个入门的了解。

项目的地址就在 gitee.com/WangFuGui-M… 如果大家对这篇文章或者专栏有兴趣或者对大家有所帮助的话,欢迎关注点赞。加评论。 我们spring security的进阶专栏见。

Spring Security入门教程:实现用户的授权功能