1.目录结构
2.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<groupId>com.cxb</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-security</name>
<description>security demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
3.application.yml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false
username: root
password: 123456
server:
port: 8081
4.security.sql(数据库脚本)
-- --------------------------------------------------------
-- 主机: 127.0.0.1
-- 服务器版本: 5.6.40 - MySQL Community Server (GPL)
-- 服务器操作系统: Win64
-- HeidiSQL 版本: 8.2.0.4675
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-- 导出 test 的数据库结构
CREATE DATABASE IF NOT EXISTS `test` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `test`;
-- 导出 表 test.role 结构
CREATE TABLE IF NOT EXISTS `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- 正在导出表 test.role 的数据:~2 rows (大约)
DELETE FROM `role`;
/*!40000 ALTER TABLE `role` DISABLE KEYS */;
INSERT INTO `role` (`id`, `rolename`) VALUES
(1, 'ROLE_USER'),
(2, 'ROLE_ADMIN');
/*!40000 ALTER TABLE `role` ENABLE KEYS */;
-- 导出 表 test.user 结构
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- 正在导出表 test.user 的数据:~2 rows (大约)
DELETE FROM `user`;
/*!40000 ALTER TABLE `user` DISABLE KEYS */;
INSERT INTO `user` (`id`, `username`, `password`) VALUES
(1, 'cxb', '$2a$10$dkBfQakqUo6ybZT7u8vxNOzG3aEPQ4iuepCidq/NZnLcUUr61fyZm'),
(2, 'frank', '$2a$10$dkBfQakqUo6ybZT7u8vxNOzG3aEPQ4iuepCidq/NZnLcUUr61fyZm');
/*!40000 ALTER TABLE `user` ENABLE KEYS */;
-- 导出 表 test.user_role 结构
CREATE TABLE IF NOT EXISTS `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- 正在导出表 test.user_role 的数据:~2 rows (大约)
DELETE FROM `user_role`;
/*!40000 ALTER TABLE `user_role` DISABLE KEYS */;
INSERT INTO `user_role` (`id`, `user_id`, `role_id`) VALUES
(1, 1, 2),
(2, 2, 1);
/*!40000 ALTER TABLE `user_role` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
5.SecurityConfig
package com.cxb.security.config;
import com.cxb.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
}
6.HelloController
package com.cxb.security.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "hello";
}
/**
* 单个角色:@Secured("ROLE_USER")
* 多个角色任意一个:@Secured({"ROLE_USER","ROLE_ADMIN"})
*/
@Secured("ROLE_USER")
@GetMapping("/user")
public String user() {
return "Hello Security ROLE_USER";
}
@Secured("ROLE_ADMIN")
@GetMapping("/admin")
public String admin() {
return "Hello Security ROLE_ADMIN";
}
/**
* 进入方法之前验证授权,支持表达式
* 允许所有访问:@PreAuthorize("true")
* 拒绝所有访问:@PreAuthorize("false")
* 单个角色:@PreAuthorize("hasRole('ROLE_USER')")
* 多个角色与条件:@PreAuthorize("hasRole('ROLE_USER') AND hasRole('ROLE_ADMIN')")
* 多个角色或条件:@PreAuthorize("hasRole('ROLE_USER') OR hasRole('ROLE_ADMIN')")
*/
@PreAuthorize("true")
@GetMapping("/authorized")
public String authorized() {
return "Hello World";
}
@PreAuthorize("false")
@GetMapping("/denied")
public String denied() {
return "Goodbye World";
}
}
7.Role
package com.cxb.security.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("role")
public class Role implements GrantedAuthority {
@TableId(type = IdType.AUTO)
private Long id;
private String rolename;
@Override
public String getAuthority() {
return this.rolename;
}
}
8.User
package com.cxb.security.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User implements UserDetails {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
@TableField(exist = false)
private List<Role> roleList;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roleList;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
9.UserRole
package com.cxb.security.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_role")
public class UserRole {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long roleId;
}
10.RoleRepository
package com.cxb.security.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cxb.security.entity.Role;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RoleRepository extends BaseMapper<Role> {
}
11.UserRepository
package com.cxb.security.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cxb.security.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserRepository extends BaseMapper<User> {
}
12.UserRoleRepository
package com.cxb.security.repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cxb.security.entity.UserRole;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserRoleRepository extends BaseMapper<UserRole> {
}
13.UserService 配置security的主要配置了
package com.cxb.security.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cxb.security.entity.Role;
import com.cxb.security.entity.User;
import com.cxb.security.entity.UserRole;
import com.cxb.security.repository.RoleRepository;
import com.cxb.security.repository.UserRepository;
import com.cxb.security.repository.UserRoleRepository;
import lombok.AllArgsConstructor;
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 java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
@Service
public class UserService implements UserDetailsService {
private UserRepository userRepository;
private RoleRepository roleRepository;
private UserRoleRepository userRoleRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username == null || username.isEmpty()) {
throw new UsernameNotFoundException("用户名不能为空");
}
User user = userRepository.selectOne(new QueryWrapper<User>().lambda().eq(User::getUsername, username));
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
List<UserRole> userRoles = userRoleRepository.selectList(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, user.getId()));
if (userRoles != null && !userRoles.isEmpty()) {
List<Long> roleIds = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList());
List<Role> roles = roleRepository.selectList(new QueryWrapper<Role>().lambda().in(Role::getId, roleIds));
user.setRoleList(roles);
}
return user;
}
}
14.Tool (BCryptPasswordEncoder加解密的工具)
package com.cxb.security.util;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class Tool {
/**
* 数据库加密解密
* @param args
*/
public static void main(String[] args) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodePass = passwordEncoder.encode("123456");
System.out.println(encodePass);
boolean matches = passwordEncoder.matches("123456", encodePass);
System.out.println(matches);
}
}
15.Application
package com.cxb.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
最后测试:http://localhost:8081/login
登陆用户frank(权限是user)
访问:http://localhost:8081/admin 失败