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 {/*** 用户验证成功处理类*/@Resourceprivate UserLoginAuthenticationSuccessHandler userLoginAuthenticationSuccessHandler;/*** 用户验证失败处理类*/@Resourceprivate UserLoginAuthenticationFailureHandler userLoginAuthenticationFailureHandler;/*** 无权限操作时的处理类*/@Resourceprivate UserAuthenticationAccessDeniedHandler userAuthenticationAccessDeniedHandler;/*** 用户登出处理类*/@Resourceprivate UserLogoutSuccessHandler userLogoutSuccessHandler;//配置安全拦截机制@Overrideprotected 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*/@Overridepublic void configure(AuthenticationManagerBuilder auth) {auth.authenticationProvider(authenticationProvider());}@Beanpublic DaoAuthenticationProvider authenticationProvider() {DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();//spring security 的登录实现daoAuthenticationProvider.setUserDetailsService(new UserDetailsServiceImpl());//使用BCrypt进行密码加密校验daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());//设置hideUserNotFoundExceptions为falsedaoAuthenticationProvider.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 {//这里通过注入,查询数据库实现登录/*@Resourceprivate UserService userService;*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//登录账号System.out.println("username="+username);//1、这里是从数据库查询出来的对象User user = new User();user.setUsername(username);//这个加密密码是456user.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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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{@Overridepublic 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();}
}
主要的是这几个配置,其他的配置可以通过源代码访问:
qw/springboot-security-demo