详解如何在项目中应用SpringSecurity权限控制

目录
  • 1、Spring Security环境准备
  • 2、实现认证和授权
  • 3、在控制器上实现注解鉴权
  • 4、请求获取当前登录的用户名信息
  • 5、用户退出

要进行认证和授权需要前面课程中提到的权限模型涉及的7张表支撑,因为用户信息、权限信息、菜单信息、角色信息、关联信息等都保存在这7张表中,也就是这些表中的数据是我们进行认证和授权的依据。所以在真正进行认证和授权之前需要对这些数据进行管理,即我们需要开发如下一些功能:

1、权限数据管理(增删改查)

2、菜单数据管理(增删改查)

3、角色数据管理(增删改查、角色关联权限、角色关联菜单)

4、用户数据管理(增删改查、用户关联角色)

数据库数据实现导入,简化上面的4步步骤

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `birthday` date DEFAULT NULL,
  `gender` varchar(1) DEFAULT NULL,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(256) DEFAULT NULL,
  `remark` varchar(32) DEFAULT NULL,
  `station` varchar(1) DEFAULT NULL,
  `telephone` varchar(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES ('1', null, null, 'admin', '$2a$10$u/BcsUUqZNWUxdmDhbnoeeobJy6IBsL1Gn/S0dMxI2RbSgnMKJ.4a', null, null, null);
INSERT INTO `t_user` VALUES ('2', null, null, 'xiaoming', '$2a$10$3xW2nBjwBM3rx1LoYprVsemNri5bvxeOd/QfmO7UDFQhW2HRHLi.C', null, null, null);
INSERT INTO `t_user` VALUES ('3', null, null, 'test', '$2a$10$zYJRscVUgHX1wqwu90WereuTmIg6h/JGirGG4SWBsZ60wVPCgtF8W', null, null, null);

DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `FK_Reference_8` (`role_id`),
  CONSTRAINT `FK_Reference_7` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES ('1', '1');
INSERT INTO `t_user_role` VALUES ('2', '2');

DROP TABLE IF EXISTS `t_permission`;
CREATE TABLE `t_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `keyword` varchar(64) DEFAULT NULL,
  `description` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_permission
-- ----------------------------
INSERT INTO `t_permission` VALUES ('1', '新增检查项', 'CHECKITEM_ADD', null);
INSERT INTO `t_permission` VALUES ('2', '删除检查项', 'CHECKITEM_DELETE', null);
INSERT INTO `t_permission` VALUES ('3', '编辑检查项', 'CHECKITEM_EDIT', null);
INSERT INTO `t_permission` VALUES ('4', '查询检查项', 'CHECKITEM_QUERY', null);
INSERT INTO `t_permission` VALUES ('5', '新增检查组', 'CHECKGROUP_ADD', null);
INSERT INTO `t_permission` VALUES ('6', '删除检查组', 'CHECKGROUP_DELETE', null);
INSERT INTO `t_permission` VALUES ('7', '编辑检查组', 'CHECKGROUP_EDIT', null);
INSERT INTO `t_permission` VALUES ('8', '查询检查组', 'CHECKGROUP_QUERY', null);
INSERT INTO `t_permission` VALUES ('9', '新增套餐', 'SETMEAL_ADD', null);
INSERT INTO `t_permission` VALUES ('10', '删除套餐', 'SETMEAL_DELETE', null);
INSERT INTO `t_permission` VALUES ('11', '编辑套餐', 'SETMEAL_EDIT', null);
INSERT INTO `t_permission` VALUES ('12', '查询套餐', 'SETMEAL_QUERY', null);
INSERT INTO `t_permission` VALUES ('13', '预约设置', 'ORDERSETTING', null);
INSERT INTO `t_permission` VALUES ('14', '查看统计报表', 'REPORT_VIEW', null);
INSERT INTO `t_permission` VALUES ('15', '新增菜单', 'MENU_ADD', null);
INSERT INTO `t_permission` VALUES ('16', '删除菜单', 'MENU_DELETE', null);
INSERT INTO `t_permission` VALUES ('17', '编辑菜单', 'MENU_EDIT', null);
INSERT INTO `t_permission` VALUES ('18', '查询菜单', 'MENU_QUERY', null);
INSERT INTO `t_permission` VALUES ('19', '新增角色', 'ROLE_ADD', null);
INSERT INTO `t_permission` VALUES ('20', '删除角色', 'ROLE_DELETE', null);
INSERT INTO `t_permission` VALUES ('21', '编辑角色', 'ROLE_EDIT', null);
INSERT INTO `t_permission` VALUES ('22', '查询角色', 'ROLE_QUERY', null);
INSERT INTO `t_permission` VALUES ('23', '新增用户', 'USER_ADD', null);
INSERT INTO `t_permission` VALUES ('24', '删除用户', 'USER_DELETE', null);
INSERT INTO `t_permission` VALUES ('25', '编辑用户', 'USER_EDIT', null);
INSERT INTO `t_permission` VALUES ('26', '查询用户', 'USER_QUERY', null);

-- ----------------------------
-- Table structure for `t_role`
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `keyword` varchar(64) DEFAULT NULL,
  `description` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES ('1', '系统管理员', 'ROLE_ADMIN', null);
INSERT INTO `t_role` VALUES ('2', '健康管理师', 'ROLE_HEALTH_MANAGER', null);

-- ----------------------------
-- Table structure for `t_role_menu`
-- ----------------------------
DROP TABLE IF EXISTS `t_role_menu`;
CREATE TABLE `t_role_menu` (
  `role_id` int(11) NOT NULL,
  `menu_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`,`menu_id`),
  KEY `FK_Reference_10` (`menu_id`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`menu_id`) REFERENCES `t_menu` (`id`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role_menu
-- ----------------------------
INSERT INTO `t_role_menu` VALUES ('1', '1');
INSERT INTO `t_role_menu` VALUES ('2', '1');
INSERT INTO `t_role_menu` VALUES ('1', '2');
INSERT INTO `t_role_menu` VALUES ('2', '2');
INSERT INTO `t_role_menu` VALUES ('1', '3');
INSERT INTO `t_role_menu` VALUES ('2', '3');
INSERT INTO `t_role_menu` VALUES ('1', '4');
INSERT INTO `t_role_menu` VALUES ('2', '4');
INSERT INTO `t_role_menu` VALUES ('1', '5');
INSERT INTO `t_role_menu` VALUES ('1', '6');
INSERT INTO `t_role_menu` VALUES ('1', '7');
INSERT INTO `t_role_menu` VALUES ('1', '8');
INSERT INTO `t_role_menu` VALUES ('1', '9');
INSERT INTO `t_role_menu` VALUES ('1', '10');
INSERT INTO `t_role_menu` VALUES ('1', '11');
INSERT INTO `t_role_menu` VALUES ('1', '12');
INSERT INTO `t_role_menu` VALUES ('1', '13');
INSERT INTO `t_role_menu` VALUES ('1', '14');
INSERT INTO `t_role_menu` VALUES ('1', '15');
INSERT INTO `t_role_menu` VALUES ('1', '16');
INSERT INTO `t_role_menu` VALUES ('1', '17');
INSERT INTO `t_role_menu` VALUES ('1', '18');
INSERT INTO `t_role_menu` VALUES ('1', '19');
INSERT INTO `t_role_menu` VALUES ('1', '20');
INSERT INTO `t_role_menu` VALUES ('1', '21');

-- ----------------------------
-- Table structure for `t_role_permission`
-- ----------------------------
DROP TABLE IF EXISTS `t_role_permission`;
CREATE TABLE `t_role_permission` (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`),
  KEY `FK_Reference_12` (`permission_id`),
  CONSTRAINT `FK_Reference_11` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
  CONSTRAINT `FK_Reference_12` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role_permission
-- ----------------------------
INSERT INTO `t_role_permission` VALUES ('1', '1');
INSERT INTO `t_role_permission` VALUES ('2', '1');
INSERT INTO `t_role_permission` VALUES ('1', '2');
INSERT INTO `t_role_permission` VALUES ('2', '2');
INSERT INTO `t_role_permission` VALUES ('1', '3');
INSERT INTO `t_role_permission` VALUES ('2', '3');
INSERT INTO `t_role_permission` VALUES ('1', '4');
INSERT INTO `t_role_permission` VALUES ('2', '4');
INSERT INTO `t_role_permission` VALUES ('1', '5');
INSERT INTO `t_role_permission` VALUES ('2', '5');
INSERT INTO `t_role_permission` VALUES ('1', '6');
INSERT INTO `t_role_permission` VALUES ('2', '6');
INSERT INTO `t_role_permission` VALUES ('1', '7');
INSERT INTO `t_role_permission` VALUES ('2', '7');
INSERT INTO `t_role_permission` VALUES ('1', '8');
INSERT INTO `t_role_permission` VALUES ('2', '8');
INSERT INTO `t_role_permission` VALUES ('1', '9');
INSERT INTO `t_role_permission` VALUES ('2', '9');
INSERT INTO `t_role_permission` VALUES ('1', '10');
INSERT INTO `t_role_permission` VALUES ('2', '10');
INSERT INTO `t_role_permission` VALUES ('1', '11');
INSERT INTO `t_role_permission` VALUES ('2', '11');
INSERT INTO `t_role_permission` VALUES ('1', '12');
INSERT INTO `t_role_permission` VALUES ('2', '12');
INSERT INTO `t_role_permission` VALUES ('1', '13');
INSERT INTO `t_role_permission` VALUES ('2', '13');
INSERT INTO `t_role_permission` VALUES ('1', '14');
INSERT INTO `t_role_permission` VALUES ('2', '14');
INSERT INTO `t_role_permission` VALUES ('1', '15');
INSERT INTO `t_role_permission` VALUES ('1', '16');
INSERT INTO `t_role_permission` VALUES ('1', '17');
INSERT INTO `t_role_permission` VALUES ('1', '18');
INSERT INTO `t_role_permission` VALUES ('1', '19');
INSERT INTO `t_role_permission` VALUES ('1', '20');
INSERT INTO `t_role_permission` VALUES ('1', '21');
INSERT INTO `t_role_permission` VALUES ('1', '22');
INSERT INTO `t_role_permission` VALUES ('1', '23');
INSERT INTO `t_role_permission` VALUES ('1', '24');
INSERT INTO `t_role_permission` VALUES ('1', '25');
INSERT INTO `t_role_permission` VALUES ('1', '26');

1、Spring Security环境准备

pom.xml中导入Spring Security的maven坐标,目前版本使用的是5.0.5.RELEASE,版本如果过高可能就会导致不同的报错

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>${spring.security.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>${spring.security.version}</version>
</dependency>

1.1、在health_backend工程的web.xml文件中配置用于整合Spring Security框架的过滤器DelegatingFilterProxy

<!--委派过滤器,用于整合其他框架-->
<filter>
  <!--整合spring security时,此过滤器的名称固定springSecurityFilterChain-->
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!--注意区分放置的位置,有严格的先后顺序-->
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2、实现认证和授权

在health_backend工程中按照Spring Security框架要求提供SpringSecurityUserService,并且实现UserDetailsService接口

package com.zcl.security;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.pojo.Permission;
import com.itheima.pojo.Role;
import com.itheima.pojo.User;
import com.zcl.service.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 项目名称:health_parent
 * 描述:SpringSecurity实现认证和授权
 *
 * @author zhong
 * @date 2022-06-24 12:09
 */
@Component
public class SpringSecurityUserService implements UserDetailsService {

    /**
     * 使用dubbo网络远程调用服务提供方查询用户数据
     */
    @Reference
    private UserService userService;

    /**
     * 根据用户名查询数据库获取用户信息
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if(user == null) {return null;};
        List<GrantedAuthority> list = new ArrayList<>();
        // 动态为当前用户授权
        Set<Role> roles = user.getRoles();
        // 遍历角色集合,为用户授予角色
        for (Role role : roles) {
            // 为用户授予角色
            list.add(new SimpleGrantedAuthority(role.getKeyword()));
            // 获取权限
            Set<Permission> permissions = role.getPermissions();
            // 遍历权限,为角色授权
            for (Permission permission : permissions) {
                list.add(new SimpleGrantedAuthority(permission.getKeyword()));
            }
        }

        /*
         * 将密码交由框架比对
         * 参数一:账号
         * 参数二:查询数据库的密码,已加密的
         * 参数三:用户角色所具有的权限
         */
        org.springframework.security.core.userdetails.User UserSecurity = new org.springframework.security.core.userdetails.User(username,user.getPassword(),list);
        return UserSecurity;
    }
}

创建远程调用的UserService接口

public interface UserService {
    /**
     * 根据登录名查询用户数据
     * @param username
     * @return
     */
    User findByUsername(String username);
}

创建接口实现类

需要注入数据访问层来完成数据的查询,用户、角色、权限各自创建一个dao和映射文件

package com.zcl.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.pojo.Permission;
import com.itheima.pojo.Role;
import com.itheima.pojo.User;
import com.zcl.dao.PermissionDao;
import com.zcl.dao.RoleDao;
import com.zcl.dao.UserDao;
import com.zcl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Set;

/**
 * 项目名称:health_parent
 * 描述:用户服务实现类
 *
 * @author zhong
 * @date 2022-06-24 14:35
 */
@Service(interfaceClass = UserService.class)
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private PermissionDao permissionDao;

    /**
     * 根据用户名称查询用户角色信息和关联的角色信息,同时需要查询角色关联的权限信息
     * @param username
     * @return
     */
    @Override
    public User findByUsername(String username) {
        // 1、查询用户基本信息,不包含用户角色信息
        User user = userDao.findByUsername(username);
        if(user == null){
            return null;
        }
        // 2、根据用户查询的用户id查询角色信息
        Integer userId = user.getId();
        Set<Role> roles = roleDao.findByUserId(userId);
        // 3、根据角色来查询权限
        for (Role role : roles) {
            Integer roleId = role.getId();
            Set<Permission> permissions = permissionDao.findByRoleId(roleId);
            // 让角色关联权限
            role.setPermissions(permissions);
        }
        // 让用还关联角色
        user.setRoles(roles);
        return user;
    }
}

创建【用户、角色、权限】数据访问层接口

注意:这三个接口都不是在同一个类中的,而是各自独立的接口类,不要写在一起

public interface UserDao {
    User findByUsername(String username);
}

public interface RoleDao {
    Set<Role> findByUserId(Integer userId);
}

public interface PermissionDao {
    Set<Permission> findByRoleId(Integer roleId);
}

查询用户的Mapper映射文件查询数据

映射文件创建在resources文件下,注意的是需要与dao接口的包一致

<?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.zcl.dao.UserDao">
    <!--根据用户名称查询用户信息-->
    <select id="findByUsername" parameterType="string" resultType="com.itheima.pojo.User">
        select *
        from t_user
        where username = #{username}
    </select>
</mapper>

根据用户id查询角色信息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.zcl.dao.RoleDao">
    <!--根据用户id查询角色信息-->
    <select id="findByUserId" resultType="com.itheima.pojo.Role" parameterType="int">
        select r.*
        from t_role r,t_user_role ur
        where r.id = ur.role_id and ur.user_id = #{user_id}
    </select>
</mapper>

根据角色id查询角色权限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.zcl.dao.PermissionDao">
    <!--根据角色id查询权限-->
    <select id="findByRoleId" resultType="com.itheima.pojo.Permission" parameterType="int">
        select p.*
        from t_permission p,t_role_permission rp
        where p.id = rp.permission_id and rp.role_id = ${role_id}
    </select>
</mapper>

修改dubbo的批量扫描

原因:默认配置的dubbo扫描的是Controller包下的,所以我们创建了一个新的包server用于存放权限实现类,而实现类刚好也是使用dubbo来远程调用接口查询数据库的,需要使用到dubbo就需要被扫描到

<!--批量扫描-->
<dubbo:annotation package="com.zcl" />

创建springSecurity.xml配置文件

与上一篇的入门案例不同的是,SpringSecurityUserService认证提供者不需要在配置文件里面创建bean交给spring容器了,因为在类的上面已经使用@Component创建,在spring容器中已经有了,通过小写字母即可引用

如果引用认证类报红就不需要管,影响不到程序,只是IDEA的检测问题

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/security
                          http://www.springframework.org/schema/security/spring-security.xsd
                            http://www.springframework.org/schema/context
                          http://www.springframework.org/schema/context/spring-context.xsd
                          http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--配置哪些资源匿名可以访问的资源(不登录页可以访问)-->
    <security:http security="none" pattern="/login.html"></security:http>
    <security:http security="none" pattern="/css/**"></security:http>
    <security:http security="none" pattern="/js/**"></security:http>
    <security:http security="none" pattern="/img/**"></security:http>
    <security:http security="none" pattern="/plugins/**"></security:http>

    <!--
        http:用于定义相关权限控制
        auto-config:是否自动配置
                        设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等
                        设置为false时需要显示提供登录表单配置,否则会报错
        use-expressions:用于指定intercept-url中的access属性是否使用表达式
    -->
    <security:http auto-config="true" use-expressions="true">
        <security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
            <security:frame-options policy="SAMEORIGIN"></security:frame-options>
        </security:headers>
        <!--
            intercept-url:定义一个拦截规则
            pattern:对哪些url进行权限控制
            access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,
				  请求的用户只需拥有其中的一个角色就能成功访问对应的URL
        -->
        <!--只需要认证通过就可以访问-->
        <security:intercept-url pattern="/pages/**" access="isAuthenticated()"/>

        <!--如果我们要使用自己指定的页面作为登录页面,必须配置登录表单-->
        <security:form-login
                login-page="/login.html"
                username-parameter="username"
                password-parameter="password"
                login-processing-url="/login.do"
                default-target-url="/pages/main.html"
                authentication-failure-url="/login.html"/>

        <!--csrf:对应CsrfFilter过滤器
            disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403) -->
        <security:csrf disabled="true"/>

        <!--
          logout:退出登录
          logout-url:退出登录操作对应的请求路径
          logout-success-url:退出登录后的跳转页面
        -->
        <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
    </security:http>
    <!--authentication-manager:认证管理器,用于处理认证操作-->
    <security:authentication-manager>
        <!--authentication-provider:认证提供者,执行具体的认证逻辑-->
        <security:authentication-provider user-service-ref="springSecurityUserService">
            <!--引用密码加密处理bean-->
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!--配置密码加密对象-->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <!--开启注解方式权限控制-->
    <security:global-method-security pre-post-annotations="enabled"/>
</beans>

注意:<security:headers>的配置,如果没有配置这个对象就会访问不了嵌套的页面,哪怕是登录了也不行

在springmvc.xml配置文件中引入springSecurity.xml配置文件

<import resource="spring-security.xml"/>

测试

账号为:admin
密码:admin
admin加密后的形式为(存储到数据库):$2a$10$LPbhiutR34wKvjv3Qb8zBu7piw5hG3.IlQMAI3e/D1Y0DJ/mMSkYa

3、在控制器上实现注解鉴权

注意:是在每一个控制器上面添加不同的权限,设置权限的只不能乱设置需要与数据库进行比对的才可以

// 添加数据
@PreAuthorize("hasAuthority('CHECKITEM_ADD')")

// 查询数据
@PreAuthorize("hasAuthority('CHECKITEM_QUERY')")

// 删除数据
@PreAuthorize("hasAuthority('CHECKITEM_DELETE')")

// 修改数据
@PreAuthorize("hasAuthority('CHECKITEM_EDIT')")

"hasAuthority('CHECKITEM_ADD')"里面的值需要与数据库的权限值一致

模拟没有权限删除数据:

1、登录第二个账户:xiaoming

2、账户中没有删除检查项,所以我们就已删除来测试

3、点击删除的时候前端页面不会提示,查看后端控制器代码,发现被拦截了,如下报错:

4、切换回有删除权限的用户,删除查看效果

3.1、完善前端页面访问权限不足提示

在前端代码中编写一个方法被多个请求不成功函数所调用

showMessage(r){
    if(r == 'Error: Request failed with status code 403'){
        //权限不足
        this.$message.error('无访问权限');
        return;
    }else{
        this.$message.error('未知错误');
        return;
    }
}

在所有的请求方法上添加一个catch事件调用函数

// 发送删除数据
axios.get("/checkitem/delete.do?id="+row.id).then((res) => {
    // ... 回调方法
}).catch ((r) => {
    this.showMessage(r);
});

当用户操作时如果没有权限那么后端控制器就会将请求拦截下来,并在后端控制台打印拦截提示,同时前端页面回根据返回的报错类型做出无权限的提示信息,从而提高用户体验

4、请求获取当前登录的用户名信息

请求控制器获取用户名信息返回页面模型绑定给username用于展示

// 发送ajax请求获取当前登录的用户名,展示到页面中
axios.get("/user/getUsername.do").then((res) => {
    if(res.data.flag){
        this.username = res.data.data;
    }
})

请求后端控制器编写

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/getUsername")
    public Result getUsername(){
        // 当springsecurity完成认证后,会将当前用户信息保存到框架提供的上下文对象中
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (user != null) {
            System.out.println(user.getUsername());
            return new Result(true, MessageConstant.GET_USERNAME_SUCCESS,user.getUsername());
        }
        return new Result(false, MessageConstant.GET_USERNAME_FAIL);
    }
}

5、用户退出

前端请求

<el-dropdown-item divided>
  <span style="display:block;"><a href="/logout.do" rel="external nofollow" >退出</a></span>
</el-dropdown-item>

后端配置文件中进行退出跳转设置

<!--
  logout:退出登录
  logout-url:退出登录操作对应的请求路径
  logout-success-url:退出登录后的跳转页面
-->
<security:logout logout-url="/logout.do"
                 logout-success-url="/login.html" invalidate-session="true"/>

到此这篇关于详解如何在项目中应用SpringSecurity权限控制的文章就介绍到这了,更多相关SpringSecurity权限控制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot整合SpringSecurity实现权限控制之实现多标签页

    目录 一.需求描述 二.前端实现 三.效果演示 四.源码 一.需求描述 多标签页 (Tabs) 的设计对于多窗口多任务管理有着无与伦比的高效率与方便性 在上面的文章中已经实现了后台管理的基本权限功能,包括用户.角色.菜单管理及权限分配. 用户通过单击侧边栏的菜单,就可以调出对应的功能页面进行使用.但在使用的过程中,我们发现程序只能在同一时间打开一个页面.我们更希望打开多个功能页面时,这些页面以标签的形式集成在同一个窗口显示,要想切换到某个页面或是关闭某个页面,我们只需要操作相应的标签即可,非常方

  • SpringSecurity权限控制实现原理解析

    菜单控制: 可以用来判断这个用户是不是有这些角色,没有的话就不展示 数据控制: 由于数据都是从后端查的,在后端控制权限就可以了 <!-- 开启权限控制注解支持 jsr250-annotations="enabled"表示支持jsr250-api的注解,需要jsr250-api的jar包 pre-post-annotations="enabled"表示支持spring表达式注解 secured-annotations="enabled"这才是

  • SpringBoot如何整合Springsecurity实现数据库登录及权限控制

    目录 第一步 第二步是封装一个自定义的类 第三步, 我们需要判断密码啦 总结 我们今天使用SpringBoot来整合SpringSecurity,来吧,不多BB 首先呢,是一个SpringBoot 项目,连接数据库,这里我使用的是mybaties.mysql, 下面是数据库的表 DROP TABLE IF EXISTS `xy_role`; CREATE TABLE `xy_role` ( `xyr_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键i

  • SpringBoot--- SpringSecurity进行注销权限控制的配置方法

    环境 IDEA :2020.1 Maven:3.5.6 SpringBoot: 2.0.9 (与此前整合的版本2.3.3 不同,版本适配问题,为配合使用降级) 1.注销 这里也有一个前提问题需要注意,我们登录操作都是在开启防跨域攻击的环境下进行的. 毫无疑问,注销也是在这样的情况下进行的. 登录时我们提交表单,采用 POST 方法传输,通过使用 Thymeleaf 在 form 表单添加 th:action 元素,Thymeleaf 会自动为我们添加 _csrf 元素. 同样注销操作也是要带有

  • Vue 动态路由的实现及 Springsecurity 按钮级别的权限控制

    思路 : 动态路由实现:在导航守卫中判断用户是否有用户信息, 通过调用接口,拿到后台根据用户角色生成的菜单树, 格式化菜单树结构信息并递归生成层级路由表并 使用Vuex保存,通过  router.addRoutes  动态挂载到  router  上,按钮级别的权限控制,则需使用自定义指令去实现. 实现: 导航守卫代码: router.beforeEach((to, from, next) => { NProgress.start() // start progress bar to.meta

  • 详解如何在项目中应用SpringSecurity权限控制

    目录 1.Spring Security环境准备 2.实现认证和授权 3.在控制器上实现注解鉴权 4.请求获取当前登录的用户名信息 5.用户退出 要进行认证和授权需要前面课程中提到的权限模型涉及的7张表支撑,因为用户信息.权限信息.菜单信息.角色信息.关联信息等都保存在这7张表中,也就是这些表中的数据是我们进行认证和授权的依据.所以在真正进行认证和授权之前需要对这些数据进行管理,即我们需要开发如下一些功能: 1.权限数据管理(增删改查) 2.菜单数据管理(增删改查) 3.角色数据管理(增删改查.

  • 详解C 语言项目中.h文件和.c文件的关系

    详解C 语言项目中.h文件和.c文件的关系 在编译器只认识.c(.cpp))文件,而不知道.h是何物的年代,那时的人们写了很多的.c(.cpp)文件,渐渐地,人们发现在很多.c(.cpp)文件中的声明语句就是相同的,但他们却不得不一个字一个字地重复地将这些内容敲入每个.c(.cpp)文件.但更为恐怖的是,当其中一个声明有变更时,就需要检查所有的.c(.cpp)文件. 于是人们将重复的部分提取出来,放在一个新文件里,然后在需要的.c(.cpp)文件中敲入#include XXXX这样的语句.这样即

  • 详解React Native项目中启用Hermes引擎

    目录 引言 一.启用 Hermes 引擎 1.1 Android 1.2 iOS 二.Hermes 引擎使用 2.1 检查 Hermes 引擎是否启用 2.2 绑定Hermes 2.3 使用DevTools在Hermes上调试JS 引言 目前,最新版本的React Native(0.70.0及以上版本)已经默认开启了Hermes引擎.而Hermes则是专门针对React Native应用而优化的全新JavaScript引擎,启用Hermes引擎可以优化启动时间,减少内存占用以及空间占用. 一.启

  • 详解在HTTPS 项目中使用百度地图 API

    百度地图 API 产品简介 百度地图 JavaScript API 是一套由 JavaScript 语言编写的应用程序接口,可帮助您在网站中构建功能丰富.交互性强的地图应用,支持 PC 端和移动端基于浏览器的地图应用开发,且支持 HTML5 特性的地图开发. 百度地图 JavaScript API 支持 HTTP 和 HTTPS,免费对外开放,可直接使用.接口使用无次数限制.在使用前,您需先申请密钥(ak)才可使用. 基础使用 引用百度地图 API, 将 "您的密匙" 替换为你在百度地

  • 详解vue在项目中使用百度地图

    第一步,去百度地图开发者申请秘钥. 第二步在项目中引入,具体如下 其中index.html存放地图链接,代码如下 然后在APP.vue里面实现,代码如下 <template> <div id="app"> <div id="allmap" ref="allmap"></div> <router-view></router-view> </div> </tem

  • 详解Spring Boot 项目中的 parent

    前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> &

  • 详解在 Angular 项目中添加 clean-blog 模板

    在 Angular 项目中添加 clean-blog 模板 clean-blog 博客模板下载 clean-blog 或者在下面链接下载 startbootstrap-clean-blog-4-dev.zip 解压并拷贝 解压下载的文件,将所有文件拷贝到 assets/clean-blog 目录下 拷贝代码 将 clean-blog 的 index.html 内容拷贝到 app.component.html <!--The whole content below can be removed w

  • 详解在vue-cli项目中使用mockjs(请求数据删除数据)

    在我们的生产实际中,后端的接口往往是较晚才会出来,于是我们的前端的许多开发都要等到接口给我们才能进行,这样对于我们前端来说显得十分的被动,于是有没有可以制造假数据来模拟后端接口呢,答案是肯定的.于是今天我们来介绍一款非常强大的插件Mock.js,可以非常方便的模拟后端的数据,也可以轻松的实现增删改查这些操作,在后台数据完成之后,你所做的只是去掉mockjs:停止拦截真实的ajax,仅此而已. 搭建一个vue项目 # 全局安装 vue-cli $ npm install --global vue-

  • 详解在Angular项目中添加插件ng-bootstrap

    npm 安装 ng-bootstrap 模块 npm install @ng-bootstrap/ng-bootstrap --save 在 Angular 项目配置 app.module.ts 添加 import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; imports: [ /** * ngx-bootstrap */ NgbModule.forRoot() ], 添加 bootstrap.min.css 样式 在 asset

  • 详解如何在项目中使用jest测试react native组件

    目前Javascript的测试工具很多,但是针对React的测试策略,Facebook推出的ReactJs标配测试工具是Jest.Jest的官网地址:https://facebook.github.io/jest/.我们可以看到Jest官网宣称的是:Painless JavaScript Testing.是Facebook用于测试服务和React应用程序的JavaScript单元测试框架. 所谓单元测试也就是对每个单元进行测试,通俗的将一般针对的是函数,类或单个组件,不涉及系统和集成.单元测试是

随机推荐