[toc]
零、项目结构
一、创建数据库
二、创建项目
1、基础配置
2、选择基础依赖
3、引入 LomBok 依赖,让开发跑的飞起
org.projectlombok
lombok
provided
4、去掉 mysql 依赖的 scope 标签
5、完整依赖
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-tomcat
provided
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
org.projectlombok
lombok
provided
三、修改application.properties 文件名为 application.yml 并写入如下配置
server:
port: 8081
spring:
datasource:
#mysql驱动类
driver-class-name: com.mysql.cj.jdbc.Driver
#数据库连接地址
url: jdbc:mysql://127.0.0.1:3306/db_spring_security_tutorial?serverTimezone=Asia/Shanghai&autoReconnect=true
#数据库账号
username: root
#密码
password: 123456
jpa:
hibernate:
#自动建表
ddl-auto: update
#方言
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
#显示sql语句
show-sql: true
四、创建实体类,并创建 UserRepository.java
1、Permission.java
@Entity
@Table(name = "permission")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String permissionName; //权限名称
public Permission(String permissionName){
this.permissionName = permissionName;
}
}
2、Role.java
@Entity
@Table(name = "role")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String roleName; //角色名称
@OneToMany(cascade = {CascadeType.ALL} , fetch = FetchType.EAGER)
@JoinColumn(name = "role_id")
List permissions;
}
3、User.java
@Entity
@Table(name = "user")
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String userName; //账号
String password; //密码
String salt; //盐
@OneToMany(cascade = {CascadeType.ALL} , fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
List roles;
}
4、UserRepository.java
@Repository
public interface UserRepository extends JpaRepository{
User findByUserName(String username);
}
五、更改账号验证方式,自定义UserDetailsService
--创建 TemplateUserDetailsService.java
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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 com.negen.entity.Permission;
import com.negen.entity.Role;
import com.negen.entity.User;
import com.negen.repository.UserRepository;
@Service
public class TemplateUserDetailsService implements UserDetailsService{
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User loginUser = userRepository.findByUserName(username);
if (null == loginUser) {
//账号不存在,抛出异常
throw new UsernameNotFoundException(username);
} else {
//用户存在,创建 SimpleGrantedAuthority集合
List authorities =
new ArrayList();
//遍历角色
for(Role role:loginUser.getRoles()) {
//遍历权限
for(Permission permission:role.getPermissions()) {
//根据权限名称创建 SimpleGrantedAuthority
SimpleGrantedAuthority authority =
new SimpleGrantedAuthority(permission.getPermissionName());
authorities.add(authority);
}
}
return new org.springframework.security.core.userdetails.User(
username, //用户名
loginUser.getPassword(), //用户密码
authorities //权限集合
);
}
}
}
六、自定义配置类
1、创建 TemplateWebSecurityConfig.java 并继承 WebSecurityConfigurerAdapter
2、重写两个 configure
3、完整代码 TemplateWebSecurityConfig.java 如下:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
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.authentication.builders.AuthenticationManagerBuilder;
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.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.negen.repository.UserRepository;
import com.negen.service.impl.TemplateUserDetailsService;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TemplateWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserRepository userRepository;
@Autowired
TemplateUserDetailsService templateUserDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/user/register",
"/swagger*//**",
"/v2/api-docs",
"/webjars*//**").permitAll() //过滤 swagger2
.anyRequest().authenticated() //配置所有除上面以为的所有请求必须认证(登录)后才能访问
.and()
.formLogin()
.loginPage("/user/login")
.loginProcessingUrl("/login") //登录接口地址
.successHandler(authenticationSuccessHandler()) //登录成功处理
.failureHandler(authenticationFailureHandler()) //登录失败处理
.permitAll();
}
// 密码加密方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setHideUserNotFoundExceptions(false); // 设置是否隐藏 UserNotFoundException
provider.setUserDetailsService(templateUserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
// 认证成功处理
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
// 认证(登录)成功
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("登录成功");
out.flush();
}
};
}
// 认证失败处理
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
if (exception instanceof UsernameNotFoundException) {
// 账号不存在
out.write("账号不存在");
out.flush();
return;
}
// 密码错误
out.write("密码错误");
out.flush();
}
};
}
}
七、创建测试类新增一条用户记录
1、UserTest.java 完整代码如下:
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.test.context.junit4.SpringRunner;
import com.negen.entity.Permission;
import com.negen.entity.Role;
import com.negen.entity.User;
import com.negen.repository.UserRepository;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
UserRepository userRepository;
@Test
public void testAddUser() {
List roles = new ArrayList();
List permissions = new ArrayList();
User user = new User();
Role role = new Role();
Permission p1 = new Permission("create");
Permission p2 = new Permission("delete");
permissions.add(p1);
permissions.add(p2);
role.setRoleName("admin");
role.setPermissions(permissions);
roles.add(role);
user.setUserName("Negen");
user.setPassword(new BCryptPasswordEncoder().encode("123456"));
user.setRoles(roles);
userRepository.save(user);
System.out.println("====>添加用户成功");
}
}
2、运行单元测试添加一位用户
八、测试
打开postman进行登录接口测试
1、账号不存在
2、密码错误
3、登录成功