SpringBoot整合SpringSecurity实现动态权限控制

一:依赖坐标

<!--springboot整合SpringSecurity-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

导入SpringSecurity坐标,启动项目便会出现权限控制的登录界面,默认用户名为user,默认密码会在控制台打印,输入账号密码即可实现登录访问;

如需修改登录的账号密码只需要在配置文件做如下配置即可实现:

spring.security.user.name=fanaozhe
spring.security.user.password=123

二:数据库

       实现一个简单的动态权限控制,总共涉及5张表,分别为user(用户)、role(角色)、user_role(用户角色关系)、menu(资源)、menu_role(资源角色);

user表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ruser
-- ----------------------------
DROP TABLE IF EXISTS `ruser`;
CREATE TABLE `ruser`  (
  `id` int(11) NOT NULL,
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `enabled` tinyint(1) NULL DEFAULT NULL COMMENT '当前账号是否可用',
  `locked` tinyint(1) NULL DEFAULT NULL COMMENT '当前账号是否锁定',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of ruser
-- ----------------------------
INSERT INTO `ruser` VALUES (1, 'root', '123', 1, 0);
INSERT INTO `ruser` VALUES (2, 'admin', '123', 1, 0);
INSERT INTO `ruser` VALUES (3, 'fanaozhe', '123', 1, 0);

SET FOREIGN_KEY_CHECKS = 1;

role表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

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

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_dba', '数据库管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户');

SET FOREIGN_KEY_CHECKS = 1;

user_role表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

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

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

SET FOREIGN_KEY_CHECKS = 1;

menu表:

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,
  `pattern` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

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

SET FOREIGN_KEY_CHECKS = 1;

menu_role表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for menu_role
-- ----------------------------
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role`  (
  `id` int(11) NOT NULL,
  `mid` int(11) NULL DEFAULT NULL,
  `rid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB 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);

SET FOREIGN_KEY_CHECKS = 1;

三:配置文件

WebSecurityConfig :

package top.fanaozhe.springbootdemo.config.securityconfig;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import top.fanaozhe.springbootdemo.service.RuserService;

/**
 * @author faz
 * @create 2020-10-24-14:32
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    RuserService ruserService;

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

    @Bean
    PasswordEncoder PasswordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    //设置角色的继承关系
    @Bean
    RoleHierarchy roleHierarchy(){
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy ="ROLE_dba > ROLE_admin > ROLE_user";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }

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

    protected void  configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {

                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(cfisms());
                        object.setAccessDecisionManager(cadm());
                        return object;
                    }
                })
                //登录页面
                .and()
                .formLogin()
                .loginProcessingUrl("/login").permitAll()

                .and()
                .csrf().disable();
    }

    @Bean
    CustomFilterInvocationSecurityMetadataSource cfisms(){
        return new CustomFilterInvocationSecurityMetadataSource();
    }
    CustomAccessDecisionManager cadm(){
        return  new CustomAccessDecisionManager();
    }

    /*//基于内存的配置;
    protected void  configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .antMatchers("/book/**").hasRole("admin")
                .antMatchers("/student/**").hasRole("dba")
                .antMatchers("/user/**").hasRole("user")
                //其他用户访问必须经过认证;
                .anyRequest()
                .authenticated()
                //登录页面
                .and()
                .formLogin()
                .loginProcessingUrl("/login").permitAll()

                .and()
                .csrf().disable();
    }*/

}

 CustomFilterInvocationSecurityMetadataSource:

package top.fanaozhe.springbootdemo.config.securityconfig;

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 top.fanaozhe.springbootdemo.entity.Menu;
import top.fanaozhe.springbootdemo.entity.Role;
import top.fanaozhe.springbootdemo.mapper.MenuMapper;

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

/**
 * @author faz
 * @create 2020-10-26-19:35
 */
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    MenuMapper menuMapper;

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        List<Menu> allMenus = menuMapper.getAllMenus();
        for (Menu menu : allMenus) {
            if (antPathMatcher.match(menu.getPattern(), requestUrl)) {
                List<Role> roles = menu.getRoles();
                String[] roleArr = new String[roles.size()];
                for (int i = 0; i < roleArr.length; i++) {
                    roleArr[i] = roles.get(i).getName();
                }
                return SecurityConfig.createList(roleArr);
            }
        }
        return SecurityConfig.createList("ROLE_LOGIN");
    }

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

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

 CustomAccessDecisionManager :

package top.fanaozhe.springbootdemo.config.securityconfig;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * @author faz
 * @create 2020-10-26-20:02
 */
public class CustomAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication auth, Object object,
                       Collection<ConfigAttribute> ca) throws AccessDeniedException, InsufficientAuthenticationException {
        Collection<? extends GrantedAuthority> auths = auth.getAuthorities();
        for (ConfigAttribute configAttribute:ca){
            if("ROLE_LOGIN".equals(configAttribute.getAttribute())&& auth instanceof UsernamePasswordAuthenticationToken){
                return;
            }
            for(GrantedAuthority authority:auths){
                if(configAttribute.getAttribute().equals(authority.getAuthority())){
                    return;
                }
            }
        }
        throw  new AccessDeniedException("权限不足");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

四:代码实现

@Mapper
public interface RuserMapper {
    /*通过用户名查找用户*/
    Ruser findUserByUsername(String username);

    /*通过用户id查找用户角色*/
    List<Role> getUserRolesByUid(Integer id);
}
<?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="top.fanaozhe.springbootdemo.mapper.RuserMapper">
    <select id="findUserByUsername" resultType="top.fanaozhe.springbootdemo.entity.Ruser">
        SELECT * FROM ruser WHERE username = #{username}
    </select>

    <select id="getUserRolesByUid" resultType="top.fanaozhe.springbootdemo.entity.Role">
        SELECT * FROM role r,ruser_role ur WHERE r.id=ur.rid and ur.uid =#{id}
    </select>

</mapper>

 

@Mapper
public interface MenuMapper {
    List<Menu> getAllMenus();
}
<?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="top.fanaozhe.springbootdemo.mapper.MenuMapper">
    <resultMap id="BaseResultMap" type="top.fanaozhe.springbootdemo.entity.Menu">
        <id property="id" column="id"/>
        <result property="pattern" column="pattern"/>
        <collection property="roles" ofType="top.fanaozhe.springbootdemo.entity.Role">
            <id property="id" column="id"/>
            <result property="name" column="rname"/>
            <result property="nameZh" column="rnameZh"/>
        </collection>
    </resultMap>

    <select id="getAllMenus" resultMap="BaseResultMap">
    select m.*,r.id as rid,r.name as rname,r.nameZh as rnameZh from menu m
    left join menu_role mr on m.id=mr.mid left join role r on mr.rid=r.id
    </select>

</mapper>

五:调试

通过以上代码即可实现一个简单的动态权限控制,实现不同路径资源访问;

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值