Springboot整合SpringSecurity的完整案例詳解
一.Spring Security介紹
Spring Security是基于Spring生態(tài)圈的,用于提供安全訪問(wèn)控制解決方案的框架。Spring Security的安 全管理有兩個(gè)重要概念,分別是Authentication(認(rèn)證)和Authorization(授權(quán))。 為了方便Spring Boot項(xiàng)目的安全管理,Spring Boot對(duì)Spring Security安全框架進(jìn)行了整合支持,并提 供了通用的自動(dòng)化配置,從而實(shí)現(xiàn)了Spring Security安全框架中包含的多數(shù)安全管理功能。
Spring Security登錄認(rèn)證主要涉及兩個(gè)重要的接口 UserDetailService和UserDetails接口。
UserDetailService接口主要定義了一個(gè)方法 loadUserByUsername(String username)用于完成用戶信息的查 詢,其中username就是登錄時(shí)的登錄名稱,登錄認(rèn)證時(shí),需要自定義一個(gè)實(shí)現(xiàn)類實(shí)現(xiàn)UserDetailService接 口,完成數(shù)據(jù)庫(kù)查詢,該接口返回UserDetail。
UserDetail主要用于封裝認(rèn)證成功時(shí)的用戶信息,即UserDetailService返回的用戶信息,可以用Spring
自己的User對(duì)象,但是最好是實(shí)現(xiàn)UserDetail接口,自定義用戶對(duì)象。
二.Spring Security認(rèn)證步驟
1. 自定UserDetails類:當(dāng)實(shí)體對(duì)象字段不滿足時(shí)需要自定義UserDetails,一般都要自定義
UserDetails。
2. 自定義UserDetailsService類,主要用于從數(shù)據(jù)庫(kù)查詢用戶信息。
3. 創(chuàng)建登錄認(rèn)證成功處理器,認(rèn)證成功后需要返回JSON數(shù)據(jù),菜單權(quán)限等。
4. 創(chuàng)建登錄認(rèn)證失敗處理器,認(rèn)證失敗需要返回JSON數(shù)據(jù),給前端判斷。
5. 創(chuàng)建匿名用戶訪問(wèn)無(wú)權(quán)限資源時(shí)處理器,匿名用戶訪問(wèn)時(shí),需要提示JSON。
6. 創(chuàng)建認(rèn)證過(guò)的用戶訪問(wèn)無(wú)權(quán)限資源時(shí)的處理器,無(wú)權(quán)限訪問(wèn)時(shí),需要提示JSON。
7. 配置Spring Security配置類,把上面自定義的處理器交給Spring Security。
三.Spring Security認(rèn)證實(shí)現(xiàn)
3.1添加Spring Security依賴
在pom.xml文件中添加Spring Security核心依賴,代碼如下所
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
3.2自定義UserDetails
當(dāng)實(shí)體對(duì)象字段不滿足時(shí)Spring Security認(rèn)證時(shí),需要自定義UserDetails。
1. 將User類實(shí)現(xiàn)UserDetails接口
2. 將原有的isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired和isEnabled屬性修 改成boolean類型,同時(shí)添加authorities屬性。
@Data @TableName("sys_user") public class User implements Serializable, UserDetails { //省略原有的屬性...... /** * 帳戶是否過(guò)期(1 未過(guò)期,0已過(guò)期) */ private boolean isAccountNonExpired = true; /** * 帳戶是否被鎖定(1 未過(guò)期,0已過(guò)期) */ private boolean isAccountNonLocked = true; /** * 密碼是否過(guò)期(1 未過(guò)期,0已過(guò)期) */ private boolean isCredentialsNonExpired = true; /** * 帳戶是否可用(1 可用,0 刪除用戶) */ private boolean isEnabled = true; /** * 權(quán)限列表 */ @TableField(exist = false) Collection<? extends GrantedAuthority> authorities;
3.3.編寫Service接口
public interface UserService extends IService<User> { /** * 根據(jù)用戶名查詢用戶信息 * @param userName * @return */ User findUserByUserName(String userName); }
3.4.編寫ServiceImpl
package com.manong.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.manong.entity.User; import com.manong.dao.UserMapper; import com.manong.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * <p> * 服務(wù)實(shí)現(xiàn)類 * </p> * * @author lemon * @since 2022-12-06 */ @Service @Transactional public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Override public User findUserByUserName(String username) { //創(chuàng)建條件構(gòu)造器對(duì)象 QueryWrapper queryWrapper=new QueryWrapper(); queryWrapper.eq("username",username); //執(zhí)行查詢 return baseMapper.selectOne(queryWrapper); } }
3.5. 自定義UserDetailsService類
package com.manong.config.security.service; import com.manong.entity.Permission; import com.manong.entity.User; import com.manong.service.PermissionService; import com.manong.service.UserService; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; 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 javax.annotation.Resource; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /* * 用戶認(rèn)證處理器類 * */ @Component public class CustomerUserDetailService implements UserDetailsService { @Resource private UserService userService; @Resource private PermissionService permissionService; public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{ //根據(jù)對(duì)象查找用戶信息 User user = userService.findUserByUserName(username); //判斷對(duì)象是否為空 if(user==null){ throw new UsernameNotFoundException("用戶的賬號(hào)密碼錯(cuò)誤"); } //查詢當(dāng)前登錄用戶擁有權(quán)限列表 List<Permission> permissionList = permissionService.findPermissionListByUserId(user.getId()); //獲取對(duì)應(yīng)的權(quán)限編碼 List<String> codeList = permissionList.stream() .filter(Objects::nonNull) .map(item -> item.getCode()) .filter(Objects::nonNull) .collect(Collectors.toList()); //將權(quán)限編碼轉(zhuǎn)換成數(shù)據(jù) String [] strings=codeList.toArray(new String[codeList.size()]); //設(shè)置權(quán)限列表 List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(strings); //將權(quán)限列表設(shè)置給User user.setAuthorities(authorityList); //設(shè)置該用戶擁有的菜單 user.setPermissionList(permissionList); //查詢成功 return user; } }
四.通常情況下,我們需要自定義四個(gè)類來(lái)獲取處理類 包括成功,失敗,匿名用戶,登錄了但沒(méi)有權(quán)限的用戶
4.1.成功
package com.manong.config.security.handler; import com.alibaba.fastjson.serializer.SerializerFeature; import com.manong.entity.User; import com.manong.utils.JwtUtils; import com.manong.utils.LoginResult; import com.manong.utils.ResultCode; import io.jsonwebtoken.Jwts; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; import sun.net.www.protocol.http.AuthenticationHeader; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import com.alibaba.fastjson.JSON; /* * 登錄認(rèn)證成功處理器類 * */ @Component public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Resource private JwtUtils jwtUtils; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //設(shè)置相應(yīng)編碼格式 response.setContentType("application/json;charset-utf-8"); //獲取當(dāng)前登錄用戶的信息 User user = (User) authentication.getPrincipal(); //創(chuàng)建token對(duì)象 String token = jwtUtils.generateToken(user); //設(shè)置token的秘鑰和過(guò)期時(shí)間 long expireTime = Jwts.parser() .setSigningKey(jwtUtils.getSecret()) .parseClaimsJws(token.replace("jwt_", "")) .getBody().getExpiration().getTime();//設(shè)置過(guò)期時(shí)間 //創(chuàng)建LOgin登錄對(duì)象 LoginResult loginResult=new LoginResult(user.getId(), ResultCode.SUCCESS,token,expireTime); //將對(duì)象轉(zhuǎn)換成json格式 //消除循環(huán)引用 String result = JSON.toJSONString(loginResult, SerializerFeature.DisableCircularReferenceDetect); //獲取輸出流 ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(result.getBytes(StandardCharsets.UTF_8)); outputStream.flush(); outputStream.close(); } }
4.2 失敗
package com.manong.config.security.handler; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.baomidou.mybatisplus.extension.api.R; import com.manong.entity.User; import com.manong.utils.Result; import com.manong.utils.ResultCode; import org.springframework.security.authentication.*; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @Component public class LoginFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { //設(shè)置相應(yīng)編碼格式 response.setContentType("application/json;charset-utf-8"); //獲取輸出流 ServletOutputStream outputStream = response.getOutputStream(); //定義變量,保存異常信息 String message=null; //判斷異常類型 if(exception instanceof AccountExpiredException){ message="賬戶過(guò)期失敗"; } else if(exception instanceof BadCredentialsException){ message="用戶名的賬號(hào)密碼錯(cuò)誤,登錄失敗"; } else if(exception instanceof CredentialsExpiredException){ message="密碼過(guò)期,登錄失敗"; } else if(exception instanceof DisabledException){ message="賬號(hào)過(guò)期,登錄失敗"; } else if(exception instanceof LockedException){ message="賬號(hào)被上鎖,登錄失敗"; } else if(exception instanceof InternalAuthenticationServiceException){ message="用戶不存在"; } else { message="登錄失敗"; } //將結(jié)果轉(zhuǎn)換為Json格式 String result = JSON.toJSONString(Result.error().code(ResultCode.ERROR).message(message)); //將結(jié)果保存到輸出中 outputStream.write(result.getBytes(StandardCharsets.UTF_8)); outputStream.flush(); outputStream.close(); } }
4.3 匿名無(wú)用戶
package com.manong.config.security.handler; import com.alibaba.fastjson.serializer.SerializerFeature; import com.manong.entity.User; import com.manong.utils.Result; import com.manong.utils.ResultCode; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; import sun.net.www.protocol.http.AuthenticationHeader; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import com.alibaba.fastjson.JSON; /* * 匿名訪問(wèn)無(wú)權(quán)限資源處理器 * */ @Component public class AnonymousAuthenticationHandler implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.setContentType("application/json;charset-utf-8"); //獲取輸出流 ServletOutputStream outputStream = response.getOutputStream(); //將對(duì)象轉(zhuǎn)換成json格式 //消除循環(huán)引用 String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("匿名用戶無(wú)權(quán)限訪問(wèn)"), SerializerFeature.DisableCircularReferenceDetect); //獲取輸出流 outputStream.write(result.getBytes(StandardCharsets.UTF_8)); outputStream.flush(); outputStream.close(); } }
4.4 登錄了但是沒(méi)有權(quán)限的用戶
package com.manong.config.security.handler; import com.alibaba.fastjson.serializer.SerializerFeature; import com.manong.entity.User; import com.manong.utils.Result; import com.manong.utils.ResultCode; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; import sun.net.www.protocol.http.AuthenticationHeader; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import com.alibaba.fastjson.JSON; /* * 認(rèn)證用戶訪問(wèn)無(wú)權(quán)限資源處理器 * */ @Component public class CustomerAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { response.setContentType("application/json;charset-utf-8"); ServletOutputStream outputStream = response.getOutputStream(); //將對(duì)象轉(zhuǎn)換成json格式 //消除循環(huán)引用 String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("用戶無(wú)權(quán)限訪問(wèn),請(qǐng)聯(lián)系教務(wù)處"), SerializerFeature.DisableCircularReferenceDetect); //獲取輸出流 outputStream.write(result.getBytes(StandardCharsets.UTF_8)); outputStream.flush(); outputStream.close(); } }
五.編寫SpringSecurity配置類,把上面的四個(gè)類進(jìn)行合并
package com.manong.config.security.service; import com.manong.config.security.handler.AnonymousAuthenticationHandler; import com.manong.config.security.handler.CustomerAccessDeniedHandler; import com.manong.config.security.handler.LoginFailureHandler; import com.manong.config.security.handler.LoginSuccessHandler; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private LoginSuccessHandler loginSuccessHandler; @Resource private LoginFailureHandler loginFailureHandler; @Resource private CustomerAccessDeniedHandler customerAccessDeniedHandler; @Resource private AnonymousAuthenticationHandler anonymousAuthenticationHandler; @Resource private CustomerUserDetailService customerUserDetailService; //注入加密類 @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } //處理登錄認(rèn)證 @Override protected void configure(HttpSecurity http) throws Exception { //登錄過(guò)程處理 http.formLogin() //表單登錄 .loginProcessingUrl("/api/user/login") //登錄請(qǐng)求url地址 .successHandler(loginSuccessHandler) //認(rèn)證成功 .failureHandler(loginFailureHandler) //認(rèn)證失敗 .and() .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不創(chuàng)建Session .and().authorizeRequests() //設(shè)置需要攔截的請(qǐng)求 .antMatchers("/api/user/login").permitAll()//登錄放行 .anyRequest().authenticated() //其他請(qǐng)求一律攔截 .and() .exceptionHandling() .authenticationEntryPoint(anonymousAuthenticationHandler) //匿名無(wú)權(quán)限類 .accessDeniedHandler(customerAccessDeniedHandler) //認(rèn)證用戶無(wú)權(quán)限 .and() .cors();//支持跨域 } //認(rèn)證配置處理器 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customerUserDetailService) .passwordEncoder(this.passwordEncoder());//密碼加密 } }
到此這篇關(guān)于Springboot整合SpringSecurity的文章就介紹到這了,更多相關(guān)Springboot整合SpringSecurity內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot啟動(dòng)security后如何關(guān)閉彈出的/login頁(yè)面
- SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用
- SpringBoot整合新版SpringSecurity完整過(guò)程
- SpringBoot集成Swagger使用SpringSecurity控制訪問(wèn)權(quán)限問(wèn)題
- SpringBoot集成SpringSecurity安全框架方式
- SpringSecurity在SpringBoot中的自動(dòng)裝配過(guò)程
- Springbootadmin與security沖突問(wèn)題及解決
- SpringBoot整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫(kù)登錄及權(quán)限控制功能
- SpringBoot配置Spring?Security的實(shí)現(xiàn)示例
相關(guān)文章
從零開始在Centos7上部署SpringBoot項(xiàng)目
本文主要介紹了從零開始在Centos7上部署SpringBoot項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Java創(chuàng)建可執(zhí)行JAR文件的多種方式
本文主要介紹了Java創(chuàng)建可執(zhí)行JAR文件的多種方式,使用JDK的jar工具、IDE、Maven和Gradle來(lái)創(chuàng)建和配置可執(zhí)行JAR文件,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07java 算法之歸并排序詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了java 算法之歸并排序詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03微信公眾號(hào)測(cè)試賬號(hào)自定義菜單的實(shí)例代碼
這篇文章主要介紹了微信公眾號(hào)測(cè)試賬號(hào)自定義菜單的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02springboot使用war包部署到外部tomcat過(guò)程解析
這篇文章主要介紹了springboot使用war包部署到外部tomcat過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01詳解SpringBoot的三種緩存技術(shù)(Spring Cache、Layering Cache 框架、Alibaba J
這篇文章主要介紹了SpringBoot的三種緩存技術(shù),幫助大家更好的理解和學(xué)習(xí)springboot框架,感興趣的朋友可以了解下2020-10-10