SpringBoot配置security权限管理
gitee:SpringBoot配置security权限管理
数据库表结构在gitee上面有
1. 目录结构
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
自定义方式:
security自带: