权限实现Security--@PreAuthorize

该文介绍了如何使用SpringSecurity的@PreAuthorize注解结合自定义权限校验类实现方法级别权限控制,以及如何创建基于角色和菜单的权限系统。通过CustomUserDetailsService加载用户、角色和菜单信息,使用@HasRole和@HasMenuPermission注解进行权限控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当使用 @PreAuthorize 注解结合自定义权限校验类实现基于方法级别的权限控制时,以下是一个完整的示例:

首先,在 SecurityService 类中添加权限校验方法 hasPermission

public boolean hasPermission(int userId, int departmentId) {
    User user = userDao.getUserById(userId);
    if (user != null) {
        return user.getDepartmentId() == departmentId && isDepartmentManager(userId, departmentId);
    }
    return false;
}

private boolean isDepartmentManager(int userId, int departmentId) {
    // 根据 userId 和 departmentId 判断该用户是否为部门经理
    // 返回 true 或 false
}

然后,在授权方法上添加 @PreAuthorize 注解:

@PreAuthorize("@securityService.hasPermission(#userId, #departmentId)")
public void grantPermission(int userId, int departmentId) {
    User user = userDao.getUserById(userId);
    if (user != null && user.getDepartmentId() == departmentId) {
        // 进行授权操作
    }
}

接下来,配置 Spring Security,以启用注解的权限控制。在 Spring Security 配置类中添加如下配置:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 配置其他 Spring Security 相关的配置

    @Bean
    public SecurityService securityService(UserDao userDao) {
        return new SecurityService(userDao);
    }
}

最后,使用 @Autowired 注解将 SecurityService 注入到其他类中,并调用授权方法:

@Autowired
private SecurityService securityService;
@Autowired
private UserDao userDao;

public void someMethod(int userId, int departmentId) {
    if (securityService.hasPermission(userId, departmentId)) {
        User user = userDao.getUserById(userId);
        if (user != null && user.getDepartmentId() == departmentId) {
            securityService.grantPermission(userId, departmentId);
        }
    } else {
        // 没有权限进行授权
    }
}

这样,通过 @PreAuthorize 注解和自定义的权限校验类,实现了基于方法级别的权限控制。在调用 grantPermission 方法之前,会自动进行权限判断,只有具有权限的用户才能执行授权操作。

接下来, 我们将定义一个基于Spring Security的角色和菜单权限控制的案例:

  1. 创建菜单实体类 Menu
public class Menu {
    private int id;
    private String name;
    private String url;
    // 其他属性

    // 省略构造函数、getter和setter方法
}
  1. 创建角色实体类 Role
public class Role {
    private int id;
    private String name;
    private List<Menu> menus;
  
    // 省略构造函数、getter和setter方法
}
  1. 创建用户实体类 User
public class User {
    private int id;
    private String username;
    private String password;
    private List<Role> roles;
  
    // 省略构造函数、getter和setter方法
}
  1. 创建用于查询用户、角色和菜单信息的相关接口:
public interface UserDao {
    User getUserByUsername(String username);
    // 其他数据库操作方法
}

public interface RoleDao {
    Role getRoleById(int roleId);
    // 其他数据库操作方法
}

public interface MenuDao {
    List<Menu> getAllMenus();
    // 其他数据库操作方法
}
  1. 创建一个实现Spring Security的 UserDetailsService 接口的类 CustomUserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.getUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("Invalid username or password.");
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                getAuthorities(user.getRoles()));
    }

    private List<GrantedAuthority> getAuthorities(List<Role> roles) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
            for (Menu menu : role.getMenus()) {
                authorities.add(new SimpleGrantedAuthority(menu.getUrl()));
            }
        }
        return authorities;
    }
}

在上述代码中,CustomUserDetailsService 类实现了 UserDetailsService 接口,并根据用户名从数据库中加载用户信息。同时,根据用户的角色和菜单信息分配相应的权限。

  1. 创建一个基于角色的权限控制注解 @HasRole
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole(@authentication, #role)")
public @interface HasRole {
    String value();
}
  1. 在控制器层(Controller)中使用 @HasRole 来实现基于角色的权限控制:
@Controller
public class MyController {

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

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

    // 其他方法
}

使用 @HasRole 注解来标记方法,通过传入指定的角色名称实现对应角色的权限控制。

除此之外,还需要在 Spring Security 的配置类中进行相关配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService userDetailsService;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/admin").hasRole("ADMIN")
            .antMatchers("/user").hasRole("USER")
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .permitAll()
            .and()
            .logout()
            .permitAll();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

在上述代码中,我们通过 configure(HttpSecurity http) 方法配置了针对 /admin/user 路径的访问权限,只有具有指定角色的用户才能访问相应的页面。同时,我们还配置了基于表单的登录和登出功能。

在上面的代码中,我们实现了基于角色的权限控制和登录认证,但还没有包含菜单权限控制。接下来我们将添加菜单权限控制的功能。

  1. CustomUserDetailsService 中,我们需要从数据库加载所有菜单信息:
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserDao userDao;
    @Autowired
    private MenuDao menuDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.getUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("Invalid username or password.");
        }
        List<Menu> menus = menuDao.getAllMenus();
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                getAuthorities(user.getRoles(), menus));
    }

    private List<GrantedAuthority> getAuthorities(List<Role> roles, List<Menu> allMenus) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
            for (Menu menu : role.getMenus()) {
                authorities.add(new SimpleGrantedAuthority(menu.getUrl()));
            }
        }
        for (Menu menu : allMenus) {
            if (!hasAuthority(authorities, menu.getUrl())) {
                authorities.add(new SimpleGrantedAuthority(menu.getUrl()));
            }
        }
        return authorities;
    }

    private boolean hasAuthority(List<GrantedAuthority> authorities, String authority) {
        for (GrantedAuthority grantedAuthority : authorities) {
            if (authority.equals(grantedAuthority.getAuthority())) {
                return true;
            }
        }
        return false;
    }
}

在上述代码中,我们在 getAuthorities 方法中将所有菜单的 url 作为权限添加到用户的权限列表中。通过检查用户的权限列表,如果不包含菜单的 url,则将其添加到用户的权限列表中。

  1. 在控制器层的方法上添加菜单权限控制的注解 @HasMenuPermission
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAuthority(#menuUrl)")
public @interface HasMenuPermission {
    String value();
}
  1. 在控制器层中使用 @HasMenuPermission 注解来实现菜单权限控制:
@Controller
public class MyController {

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

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

    @GetMapping("/menu1")
    @HasMenuPermission("/menu1")
    public String menu1Page() {
        return "menu1";
    }

    @GetMapping("/menu2")
    @HasMenuPermission("/menu2")
    public String menu2Page() {
        return "menu2";
    }

    // 其他方法
}

使用 @HasMenuPermission 注解标记方法,并传入相应菜单的 url,实现菜单级别的权限控制。

通过以上步骤,我们实现了基于角色和菜单的权限控制和登录认证,@HasRole 注解用于角色的权限控制,@HasMenuPermission 注解用于菜单的权限控制。

请注意,以上示例中的内容是一个简化的示例,实际项目中可能涉及到更复杂的业务逻辑和数据库操作。这个示例提供了一个基本框架,你可以根据实际需求进行扩展和优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值