SpringBoot配置security权限管理

gitee:SpringBoot配置security权限管理

数据库表结构在gitee上面有

1. 目录结构

Springboot安全管理器加入SecurityManager_mysql

2. maven依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <mybatis.version>2.0.1.RELEASE</mybatis.version>
        <mysql.version>5.1.47</mysql.version>
    </properties>

    <dependencies>
        <!-- 以下是>spring boot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 以下是>spring security依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- 以下是jsp依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--jsp页面使用jstl标签 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--用于编译jsp -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.easybest</groupId>
            <artifactId>spring-data-mybatis-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
    </dependencies>

3.配置文件application.yaml

server:
  port: 8080
  servlet:
    context-path: /

# 配置试图解析器地址,前后缀
spring:
  application:
    name: springboot-security
  mvc:
    view:
      prefix: /WEB-INF/view/
      suffix: .jsp
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/spring_security
    username: root
    password: root

4.entity

用户信息表实体

import lombok.Data;

/**
 * @author 黔程似景
 * @description 用户信息表
 * @date 2021/12/11 14:40
 **/
@Data
public class UserDto {

    private Integer id;

    private String username;

    private String password;

    private String mobile;
}

权限表实体

import lombok.Data;

/**
 * @author 黔程似景
 * @description 权限表
 * @date 2021/12/11 14:54
 **/
@Data
public class PermissionDto {

    private String id;

    private String code;

    private String description;

    private String url;
}

5.配置类

配置登录页面WebConfig

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 黔程似景
 * @description 配置web启动器
 * @date 2021/12/11 12:55
 **/
@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 重写视图
     * @param registry 视图控制
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //使用security自带的登录页面
        registry.addViewController("/").setViewName("redirect:/login");

        //自定义登录页面
//        registry.addViewController("/").setViewName("redirect:/login-view");
//        registry.addViewController("/login-view").setViewName("/login");
    }
}

配置用户认证权限管理WebSecurityConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;

/**
 * @author 黔程似景
 * @description 配置security权限认证
 * @date 2021/12/11 13:00
 **/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)//开启权限注解扫描
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsService userDetailsService;

    /**
     * 密码编码器
     * 1. 不使用加密算法 NoOpPasswordEncoder.getInstance()
     * @return 加密编码格式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 将密码编码器交给UserDetailsService进行使用
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        return daoAuthenticationProvider;
    }

    /**
     * 安全拦截机制
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling().accessDeniedPage("/error.html");//配置没有权限访问跳转的自定义页面
        http.csrf().disable()//需要屏蔽掉csrf安全机制,也可以在前端进行屏蔽
                .authorizeRequests()
                .antMatchers("/r/**").authenticated()//所有/r/**的请求必须通过认证(必须在子配置之后,否则会出现经过此次权限认证后,不在进行验证)
                .anyRequest().permitAll()//除了/r/**的都可以不认证访问
                .and()
                .formLogin()//允许表单登录
//                .loginPage("/login-view")//使用自带的不需要在指定登录页面
                .loginProcessingUrl("/login")
                .successForwardUrl("/login-success")
                .defaultSuccessUrl("/login-success")

                //自定义退出
                .and()
                .logout().logoutUrl("/logout")//配置退出地址
//                .logoutSuccessUrl("/login-view?logout");//退出成功返回页面(使用自带的,删除此行)
    }
}

6.mapper

import com.qq.entity.PermissionDto;
import com.qq.entity.UserDto;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;

/**
 * @author 黔程似景
 * @description 用户mapper
 * @date 2021/12/11 14:37
 **/
public interface UserMapper {
    
    /**
     * 根据用户名查询用户信息,只取第一条
     *
     * @param username 用户名
     * @return 用户详情
     */
    @Select("SELECT * FROM t_user WHERE username = #{username} ORDER BY id ASC LIMIT 1")
    UserDto getUserDtoByUsername(@Param("username") String username);

    /**
     * 获取用户权限
     */
    @Select("SELECT * FROM t_permission WHERE id in (" +
            "SELECT permission_id from t_role_permission WHERE role_id in (" +
            "SELECT role_id FROM t_user_role WHERE user_id = #{userId}" +
            ")" +
            ")")
    List<PermissionDto> getPermissionListByUserId(@Param("userId") Integer userId);
}

7.service

import com.qq.entity.PermissionDto;
import com.qq.entity.UserDto;
import com.qq.mapper.UserMapper;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 黔程似景
 * @description 进行用户信息注入,权限注入操作
 * @date 2021/12/12 9:42
 **/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //根据用户名获取用户信息
        UserDto userDto = userMapper.getUserDtoByUsername(username);

        //权限
        String[] permissionArray = this.getPermissionList(userDto.getId());

        //设置返回值
        UserDetails userDetails = null;
        if (userDto != null) {
            userDetails = User.withUsername(username).password(userDto.getPassword()).authorities(permissionArray).build();
        }

        return userDetails;
    }

    /**
     * 获取权限数组
     */
    private String[] getPermissionList(Integer userId) {
        List<PermissionDto> permissionDtoList = userMapper.getPermissionListByUserId(userId);
        List<String> permissionList = new ArrayList<>();

        if (permissionDtoList != null) {
            permissionDtoList.forEach(permissionDto -> permissionList.add(permissionDto.getCode()));
        }

        String[] permissionArray = permissionList.toArray(new String[]{});
        return permissionArray;
    }
}

8.controller

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 黔程似景
 * @description 有关于登录,测试接口
 * @date 2021/12/11 13:17
 **/
@RestController
public class LoginController {

    @GetMapping("/login-success")
    public String loginSuccess() {
        //提示登录成功
        return "登录成功!";
    }

    @GetMapping("/r/r1")
    @PreAuthorize("hasAnyAuthority('p1')")//拥有P1权限才可以访问
    public String r1() {
        return getUserName() + "访问资源1";
    }

    @GetMapping("/r/r2")
    @PreAuthorize("hasAnyAuthority('p2')")//拥有P2权限才可以访问
    public String r2() {
        return getUserName() + "访问资源2";
    }

    //获取当前用户信息
    private String getUserName() {
        String username;
        //当前认证通过的用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //用户身份
        Object principal = authentication.getPrincipal();
        if (principal == null) {
            username = "匿名";
        }

        if (principal instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) principal;
            username = userDetails.getUsername();
        } else {
            username = principal.toString();
        }

        return username;
    }

}

9.自定义登录页面

登录页面

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html>
<head>
    <title>用户登录</title>
</head>
<body>
<form action="login" method="post">
    用户名:<input type="text" name="username"><br>
    密   码:
    <input type="password" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

权限不足页面

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html>
<head>
    <title>不拥有此权限</title>
</head>
<body>
<h5>权限不够!联系管理员</h5>
</body>
</html>

10.启动类

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * springboot项目 启动类
 */
@SpringBootApplication
@MapperScan("com.qq.mapper")
public class SecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }
}

11.测试

退出当前用户:http://localhost:8080/logout

自定义方式:

Springboot安全管理器加入SecurityManager_ide_02

Springboot安全管理器加入SecurityManager_spring_03


Springboot安全管理器加入SecurityManager_spring boot_04


Springboot安全管理器加入SecurityManager_spring_05

security自带:

Springboot安全管理器加入SecurityManager_ide_06

Springboot安全管理器加入SecurityManager_ide_07

Springboot安全管理器加入SecurityManager_ide_08


Springboot安全管理器加入SecurityManager_spring_09