1、本文章中SpringBoot整合SpringSecurity,只是基于session方式,并且没有使用到redis。
2、登录、登出都是通过ajax的方式进行。
项目目录:
1、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 https://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.3.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.springboot.security.demo</groupId>
<artifactId>springboot-security-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-security-demo</name>
<description>springboot-security project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--spring-boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- thymeleaf 模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- spring security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<!--谷歌处理json的工具包-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、SecurityConfig 配置类
package com.example.springboot.security.demo.config.security;
import com.example.springboot.security.demo.handle.UserAuthenticationAccessDeniedHandler;
import com.example.springboot.security.demo.handle.UserLoginAuthenticationFailureHandler;
import com.example.springboot.security.demo.handle.UserLoginAuthenticationSuccessHandler;
import com.example.springboot.security.demo.handle.UserLogoutSuccessHandler;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.annotation.Resource;
/**
* 配置 Security
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//开启security的权限校验注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 用户验证成功处理类
*/
@Resource
private UserLoginAuthenticationSuccessHandler userLoginAuthenticationSuccessHandler;
/**
* 用户验证失败处理类
*/
@Resource
private UserLoginAuthenticationFailureHandler userLoginAuthenticationFailureHandler;
/**
* 无权限操作时的处理类
*/
@Resource
private UserAuthenticationAccessDeniedHandler userAuthenticationAccessDeniedHandler;
/**
* 用户登出处理类
*/
@Resource
private UserLogoutSuccessHandler userLogoutSuccessHandler;
//配置安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();//禁用CSRF控制,即spring security不再限制CSRF,即跨越访问
http
.authorizeRequests()
.antMatchers("/static/**").permitAll()//不需要登录认证就可以访问,静态资源等不需要验证
.antMatchers("/public/**").anonymous()//以public开头的接口,都可以匿名访问,即不用登录就可以访问
.anyRequest().authenticated();//其他路径必须验证身份
http
.formLogin()
.loginPage("/login-view.html")//登录页面,加载登录的html页面
.loginProcessingUrl("/login")//发送Ajax请求的路径
.usernameParameter("username")//请求验证参数
.passwordParameter("password")//请求验证参数
.successHandler(userLoginAuthenticationSuccessHandler)//验证成功处理
.failureHandler(userLoginAuthenticationFailureHandler)//验证失败处理
.permitAll();//登录页面无需设置验证
http
.logout()
.logoutUrl("/logout")//登出路径
.logoutSuccessHandler(userLogoutSuccessHandler)//登出处理
.permitAll()//不需要身份认证
.and()
.exceptionHandling().accessDeniedHandler(userAuthenticationAccessDeniedHandler);//无权限时的处理
}
/**
* 配置身份验证管理器
* @param auth
*/
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
//spring security 的登录实现
daoAuthenticationProvider.setUserDetailsService(new UserDetailsServiceImpl());
//使用BCrypt进行密码加密校验
daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
//设置hideUserNotFoundExceptions为false
daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
return daoAuthenticationProvider;
}
}
3、UserDetailsServiceImpl:spring security 的登录实现
package com.example.springboot.security.demo.config.security;
import com.example.springboot.security.demo.model.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.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
/**
* spring security 的登录实现
*/
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
//这里通过注入,查询数据库实现登录
/*@Resource
private UserService userService;*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//登录账号
System.out.println("username="+username);
//1、这里是从数据库查询出来的对象
User user = new User();
user.setUsername(username);
//这个加密密码是456
user.setPassword("$2a$10$zgnAvX32nq.NaWtQ0SrMGOiJUH4jtTCXtLHWPPWBnHP4knzndbROm");
//保存用户账号到session中,这样在controller中就可以直接获取
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
request.setAttribute("username",username);
//模拟用户查询出来有问题
if (username.equals("zhangsan")){
throw new UsernameNotFoundException("用户不存在");
}
if (username.equals("zhangsan2")){
throw new UsernameNotFoundException("用户被禁用");
}
//从数据库中查询用户的权限信息
List<String> permissions = new ArrayList<>();
permissions.add("sysUserInfo:view");
permissions.add("sysUserInfo:update");
permissions.add("sysUserInfo:delete");
user.setPermissionList(permissions);
//如果不重写UserDetails,那么可以用下面的这种写法
// String[] permissionArray = new String[permissions.size()];
// permissions.toArray(permissionArray);
// UserDetails userDetails = org.springframework.security.core.userdetails.User.withUsername(username).password("123").authorities(permissionArray).build();
return user;
}
}
4、无权限操作时的处理类
package com.example.springboot.security.demo.handle;
import com.example.springboot.security.demo.common.JsonData;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 无权限操作时的处理类
*/
@Slf4j
@Component("UserAuthenticationAccessDeniedHandler")
public class UserAuthenticationAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AccessDeniedException e) throws IOException, ServletException {
log.info("{}","无权限处理");
JsonData jsonData = new JsonData(500,"error");
String json = new Gson().toJson(jsonData);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(json);
out.flush();
out.close();
}
}
5、用户验证失败处理类
package com.example.springboot.security.demo.handle;
import com.example.springboot.security.demo.common.JsonData;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 用户验证失败处理类
*/
@Slf4j
@Component("UserLoginAuthenticationFailureHandler")
public class UserLoginAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String username = (String) request.getAttribute("username");
System.out.println("登录用户:"+username);
JsonData jsonData = null;
/**
* UserDetailsService 校验用户时,主动抛出的错误,如:用户信息不存在 用户被禁用等
*/
if (exception instanceof UsernameNotFoundException){
jsonData = new JsonData(402,exception.getMessage());
}
/**
* 密码错误
*/
if (exception instanceof BadCredentialsException){
log.info(exception.getMessage());
jsonData = new JsonData(403,"账号或密码错误");
// String user_name = userService.findByUserNameAttemps(username);
// if (user_name == null){
// String time = DateUtil.getTimeToString();
// UserLoginAttempts userLoginAttempts = new UserLoginAttempts(username,1,time);
// userService.saveAttempts(userLoginAttempts);
// }
// if(userService.getAttempts(username) == 1){
// String time = DateUtil.getTimeToString();
// userService.setAttempts(username,time);
// jsonData = new JsonData(403,"密码错误,你还有2次机会进行登录操作");
// }
// else if(userService.getAttempts(username) == 3){
// User user = userService.findByUserName(username);
// userService.LockUser(user.getId());
// jsonData = new JsonData(403,"最后一次尝试登陆失败,你已经被冻结了");
// }
// else if (userService.getAttempts(username) ==2 ){
// String time = DateUtil.getTimeToString();
// userService.setAttempts(username,time);
// jsonData = new JsonData(403,"密码错误,你还有1次机会进行登录操作");
// }
}
String json = new Gson().toJson(jsonData);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(json);
out.flush();
out.close();
}
}
6、用户验证成功处理类
package com.example.springboot.security.demo.handle;
import com.example.springboot.security.demo.common.JsonData;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 用户验证成功处理类
*/
@Slf4j
@Component("UserLoginAuthenticationSuccessHandler")
public class UserLoginAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
JsonData jsonData = new JsonData(200,"认证OK");
String json = new Gson().toJson(jsonData);
log.info("{}","handle_success");
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(json);
out.flush();
out.close();
}
}
7、用户登出处理类
package com.example.springboot.security.demo.handle;
import com.example.springboot.security.demo.common.JsonData;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 用户登出处理类
*/
@Slf4j
@Component("UserLogoutSuccessHandler")
public class UserLogoutSuccessHandler implements LogoutSuccessHandler{
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
JsonData jsonData = new JsonData(200,"退出成功");
String json = new Gson().toJson(jsonData);
log.info("{}","LogOut*******Success");
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(json);
out.flush();
out.close();
}
}
主要的是这几个配置,其他的配置可以通过源代码访问: