SpringBoot實現登錄攔截器超詳細教程分享
SpringBoot實現登錄攔截器
對于管理系統或其他需要用戶登錄的系統,登錄驗證都是必不可少的環(huán)節(jié),在 SpringBoot 開發(fā)的項目中,通過實現攔截器來實現用戶登錄攔截并驗證。
SpringBoot 實現登錄攔截的原理
SpringBoot 通過實現HandlerInterceptor接口實現攔截器,通過實現WebMvcConfigurer接口實現一個配置類,在配置類中注入攔截器,最后再通過 @Configuration 注解注入配置.
1.實現HandlerInterceptor接口
實現HandlerInterceptor接口需要實現 3 個方法:preHandle、postHandle、afterCompletion.
3 個方法各自的功能如下:
public class UserLoginInterceptor implements HandlerInterceptor {
/***
* 在請求處理之前進行調用(Controller方法調用之前)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("執(zhí)行了攔截器的preHandle方法");
try {
HttpSession session = request.getSession();
//統一攔截(查詢當前session是否存在user)(這里user會在每次登錄成功后,寫入session)
User user = (User) session.getAttribute(USER_LOGIN_STATE);
if (user != null) {
return true;
}
//重定向登錄頁面
response.sendRedirect(request.getContextPath() + "/user/login");
} catch (Exception e) {
e.printStackTrace();
}
return false;
//如果設置為false時,被請求時,攔截器執(zhí)行到此處將不會繼續(xù)操作
//如果設置為true時,請求將會繼續(xù)執(zhí)行后面的操作
}
/***
* 請求處理之后進行調用,但是在視圖被渲染之前(Controller方法調用之后)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("執(zhí)行了攔截器的postHandle方法");
}
/***
* 整個請求結束之后被調用,也就是在DispatchServlet渲染了對應的視圖之后執(zhí)行(主要用于進行資源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("執(zhí)行了攔截器的afterCompletion方法");
}
}preHandle在 Controller 之前執(zhí)行,因此攔截器的功能主要就是在這個部分實現:
1.檢查 session 中是否有user對象存在;
2.如果存在,就返回true,那么 Controller 就會繼續(xù)后面的操作;
3.如果不存在,就會重定向到登錄界面
就是通過這個攔截器,使得 Controller 在執(zhí)行之前,都執(zhí)行一遍preHandle.
2.實現WebMvcConfigurer接口,注冊攔截器
實現WebMvcConfigurer接口來實現一個配置類,將上面實現的攔截器的一個對象注冊到這個配置類中.
@Configuration
public class LoginConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注冊TestInterceptor攔截器
InterceptorRegistration registration = registry.addInterceptor(new UserLoginInterceptor());
//所有路徑都被攔截
registration.addPathPatterns("/**");
//添加不攔截路徑
registration.excludePathPatterns(
"/user/login",
"/user/register",
"/**/*.html",
"/**/*.js",
"/**/*.css"
);
}
}
將攔截器注冊到了攔截器列表中,并且指明了攔截哪些訪問路徑,不攔截哪些訪問路徑,不攔截哪些資源文件;最后再以 @Configuration 注解將配置注入。
3.保持登錄狀態(tài)
只需一次登錄,如果登錄過,下一次再訪問的時候就無需再次進行登錄攔截,可以直接訪問網站里面的內容了。
在正確登錄之后,就將user保存到session中,再次訪問頁面的時候,登錄攔截器就可以找到這個user對象,就不需要再次攔截到登錄界面了.
UserController
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
/**
* 發(fā)送郵箱驗證碼
* @return
*/
@PostMapping("/sendCode")
public BaseResponse<String> sendCode(@RequestBody String email) {
// 發(fā)送短信驗證碼并保存驗證碼
String code = userService.sendCode(email);
return ResultUtils.success(code);
}
/**
* 注冊功能
* @param userRegisterRequest
* @return
*/
@PostMapping("/register")
public BaseResponse<Long> register(@RequestBody UserRegisterRequest userRegisterRequest){
if(userRegisterRequest==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"請求參數為空");
}
String email = userRegisterRequest.getEmail();
String userpassword = userRegisterRequest.getUserPassword();
String checkpassword = userRegisterRequest.getCheckPassword();
String userName = userRegisterRequest.getName();
String code = userRegisterRequest.getCode();
if(StringUtils.isAnyBlank(email,userpassword,checkpassword,userName,code)){
return null;
}
long result = userService.userRegister(email, userpassword, checkpassword, userName, code);
return ResultUtils.success(result);
}
/**
* 登錄功能
* @param userLoginRequest
* @param request
* @return
*/
@PostMapping("/login")
public BaseResponse<User> userdoLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request){
if(userLoginRequest==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"請求參數為空");
}
String email = userLoginRequest.getEmail();
String password = userLoginRequest.getPassword();
if (StringUtils.isAnyBlank(email,password)){
return null;
}
User result = userService.userdoLogin(email, password,request);
return ResultUtils.success(result);
}
/**
* 登出功能
* @param request
* @return
*/
@PostMapping("/logout")
public BaseResponse<Integer> userlogout(HttpServletRequest request){
if(request==null){
throw new BusinessException(ErrorCode.NOT_LOGIN,"該用戶沒有登錄");
}
int result = userService.userLogout(request);
return ResultUtils.success(result);
}
UserService
public interface UserService extends IService<User> {
/**
* 發(fā)送驗證碼
* @param email
* @return
*/
String sendCode(String email);
/**
* 用戶注冊
*
* @param userEmail 用戶郵箱
* @param userPassword 用戶密碼
* @param checkPassword 用戶檢驗密碼
*
* @return
*/
long userRegister(String userEmail,String userPassword,String checkPassword,String userName,String code);
/**
* 用戶登錄
* @param email
* @param password
* @param request
* @return
*/
User userdoLogin(String email, String password, HttpServletRequest request);
/**
* 用戶登出
* @param request
* @return
*/
int userLogout(HttpServletRequest request);
UserServiceImpl
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper,User>
implements UserService{
@Resource
private UserMapper userMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
public static final String SALT = "qgc";
/**
* 發(fā)送郵箱驗證碼
* @param email
* @return
*/
@Override
public String sendCode(String email) {
//1.生成驗證碼
String code = RandomUtil.randomNumbers(6);
//2.保存驗證碼到redis中 //set key value ex
stringRedisTemplate.opsForValue().set(code + LOGIN_CODE_KEY, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
//3.發(fā)送驗證碼
log.debug("發(fā)送郵箱驗證碼成功,驗證碼:{}", code);
return code;
}
/**
* 用戶注冊
* @param email 郵箱
* @param userPassword 用戶密碼
* @param checkPassword 用戶檢驗密碼
*
* @param userName 用戶名字
* @param code 驗證碼
* @return
*/
@Override
public long userRegister(String email,String userPassword,String checkPassword,String userName,String code) {
//1.校驗
if(StringUtils.isAnyBlank(email,userPassword,checkPassword,userName,code)){
throw new BusinessException(PARAMS_ERROR,"請求參數為空");
}
if(userPassword.length() < 8 ||checkPassword.length() < 8){
throw new BusinessException(PARAMS_ERROR,"密碼小于8位");
}
if(userName.length()> 10){
throw new BusinessException(PARAMS_ERROR,"名字大于10位");
}
if(code.length() != 6){
throw new BusinessException(PARAMS_ERROR,"驗證碼長度應該為6位");
}
//密碼和校驗密碼相同
if(!userPassword.equals(checkPassword)){
throw new BusinessException(PARAMS_ERROR);
}
//賬戶郵箱不能重復
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("email",email);
Long count = userMapper.selectCount(queryWrapper);
if (count>0){
throw new BusinessException(PARAMS_ERROR);
}
//昵稱不能重復
queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name",userName);
count = userMapper.selectCount(queryWrapper);
if (count>0){
throw new BusinessException(PARAMS_ERROR);
}
//判斷驗證碼是否正確
String cachecode = stringRedisTemplate.opsForValue().get(code + LOGIN_CODE_KEY);
if(cachecode==null||!cachecode.equals(code)){
//不一致,報錯
throw new BusinessException(PARAMS_ERROR);
}
//2.加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes(StandardCharsets.UTF_8));
//3.插入數據
User user = new User();
user.setEmail(email);
user.setPassword(encryptPassword);
user.setName(userName);
boolean res = this.save(user);
if(!res){
return -1;
}
return user.getId();
}
/**
* 用戶登錄
* @param email
* @param password
* @param request
* @return
*/
@Override
public User userdoLogin(String email, String password,HttpServletRequest request) {
//1.校驗
if(StringUtils.isAnyBlank(email,password)){
return null;
}
if (RegexUtils.isEmailInvalid(email)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "郵箱格式錯誤");
}
if(password.length() < 8 ){
return null;
}
//2.加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes(StandardCharsets.UTF_8));
//判斷賬戶是否存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("email",email);
queryWrapper.eq("password",encryptPassword);
User user = userMapper.selectOne(queryWrapper);
if(user==null){
log.info("user login failed");
return null;
}
//用戶脫敏
User safeUser = getSafeUser(user);
//4.記錄用戶登錄狀態(tài)
request.getSession().setAttribute(USER_LOGIN_STATE,safeUser);
return safeUser;
}
/**
* 登出功能
* @param request
* @return
*/
@Override
public int userLogout(HttpServletRequest request) {
request.getSession().removeAttribute(USER_LOGIN_STATE);
return 1;
}
springboot攔截器excludePathPatterns方法不生效的坑與解決方法
最近在springboot項目里需要配置個攔截器白名單,用excludePathPatterns方法配置些url,讓攔截器不攔截這些url;
本來這是個很簡單的東西,但是配置完畢后就是沒有生效;
在此記錄下這個坑的解決方法。
問題
1.例如,想讓以下url不被攔截器攔截:
http://localhost:8080/api/department/add
2.攔截器配置代碼如下:
@Configuration
public class LoginConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注冊TestInterceptor攔截器
InterceptorRegistration registration = registry.addInterceptor(new UserLoginInterceptor());
//所有路徑都被攔截
registration.addPathPatterns("/**");
//添加不攔截路徑
registration.excludePathPatterns(
"/user/login",
"/user/register",
"/api/department/add"
"/**/*.html",
"/**/*.js",
"/**/*.css"
);
}
}
3.看起來沒有問題,但是當訪問上方url的時候,還是會被攔截器攔截,就很坑。
解決方法
1.通過排查發(fā)現,原來,在application.yml中,是這樣配置的:
server:
port: 8080
servlet:
context-path: /api
2.所以,還是攔截器的url配置錯了,想不攔截的話,需要這樣配置:
@Configuration
public class LoginConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注冊TestInterceptor攔截器
InterceptorRegistration registration = registry.addInterceptor(new UserLoginInterceptor());
//所有路徑都被攔截
registration.addPathPatterns("/**");
//添加不攔截路徑
registration.excludePathPatterns(
"/user/login",
"/user/register",
"/department/add"
"/**/*.html",
"/**/*.js",
"/**/*.css"
);
}
}
3.這樣,訪問這個url,才能不被攔截器攔截:
http://localhost:8080/survey-project/download/special
總結
1.配置攔截器時,如果excludePathPatterns沒有生效,可能是url配置有問題。
2.可以檢查下application.yml的context-path,或者其它類似的地方,配置攔截器的url不應該包含這些路徑,只要從Controller的路徑開始配置即可。
擴展
使用response對象的sendRedirect()方法將用戶的請求重定向到指定路徑,這個路徑由request對象的getContextPath()方法獲取,再加上字符串 “/” 組成。getContextPath()方法返回當前web應用程序的上下文路徑,此處加的字符串路徑也是從Controller的路徑開始配置即可
“/user/login”
//重定向登錄頁面 response.sendRedirect(request.getContextPath() + "/user/login");
會被重定向到
http://127.0.0.1:8080/user/login

“/login”
//重定向登錄頁面 response.sendRedirect(request.getContextPath() + "/login");
http://127.0.0.1:8080/login

"/user/login"也是從Controller的路徑開始配置
以上就是SpringBoot實現登錄攔截器超詳細教程分享的詳細內容,更多關于SpringBoot登錄攔截器的資料請關注腳本之家其它相關文章!
相關文章
Exception in thread main java.lang.NoClassDefFoundError錯誤解決方
這篇文章主要介紹了Exception in thread main java.lang.NoClassDefFoundError錯誤解決方法,需要的朋友可以參考下2016-08-08
使用HandlerMethodArgumentResolver用于統一獲取當前登錄用戶
這篇文章主要介紹了使用HandlerMethodArgumentResolver用于統一獲取當前登錄用戶實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
Spring Boot使用線程池創(chuàng)建多線程的完整示例
在 Spring Boot 2 中,可以使用 @Autowired 注入 線程池(ThreadPoolTaskExecutor 或 ExecutorService),從而管理線程的創(chuàng)建和執(zhí)行,以下是使用 @Autowired 方式注入線程池的完整示例,感興趣的朋友一起看看吧2025-03-03
基于spring-mvc.xml和application-context.xml的配置與深入理解
這篇文章主要介紹了spring-mvc.xml和application-context.xml的配置與深入解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
完全解析Java編程中finally語句的執(zhí)行原理
這篇文章主要深度介紹了Java編程中finally語句的執(zhí)行原理,細致講解了finally在異常處理中的流程控制作用,需要的朋友可以參考下2015-11-11
詳解spring cloud config整合gitlab搭建分布式的配置中心
這篇文章主要介紹了詳解spring cloud config整合gitlab搭建分布式的配置中心,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01

