SpringBoot?Security從入門到實戰(zhàn)示例教程
前言
Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。提供了完善的認證機制和方法級的授權(quán)功能。是一款非常優(yōu)秀的權(quán)限管理框架。它的核心是一組過濾器鏈,不同的功能經(jīng)由不同的過濾器。這篇文章給大家講解SpringBoot Security從入門到實戰(zhàn),內(nèi)容如下所示:
入門
測試接口
假設(shè)我們用下面的接口做權(quán)限測試。
@RestController
public class LakerController {
@GetMapping("/laker")
public String laker() {
return IdUtil.simpleUUID();
}
@GetMapping("/laker/q")
public String lakerQ() {
return IdUtil.simpleUUID();
}
}瀏覽器訪問:http://localhost:8080/laker,結(jié)果如下:

增加依賴
在 pom.xml,添加 spring-boot-starter-securtiy 依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
再次訪問http://localhost:8080/laker,結(jié)果如下:

簡要解析
我們訪問http://localhost:8080/laker,security判斷我們沒有登錄,則會302重定向到http://localhost:8080/login(默認)

- security會返回一個默認的登錄頁。
- 默認用戶名為:
user,密碼在服務(wù)啟動時,會隨機生成一個,可以查看啟動日志如下:
2022-05-02 21:01:03.697 INFO 17896 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2022-05-02 21:01:03.825 INFO 17896 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :Using generated security password: e53fef6a-3f61-43c3-9609-ce88fd7c0841
當然,可以通過配置文件設(shè)置默認的用戶名、密碼、角色。
spring:
security:
user:
# 默認是 user
name: laker
password: laker
roles:
- ADMIN
- TESTER以上的默認都是可以修改的哈。
判斷是否登錄使用傳統(tǒng)的cookie session模式哈,JSESSIONID E3512CD1A81DB7F2144C577BA38D2D92 HttpOnly true
自定義配置
實際項目中我們的用戶、密碼、角色、權(quán)限、資源都是存儲在數(shù)據(jù)庫中的,我們可以通過自定義類繼承 WebSecurityConfigurerAdapter,從而實現(xiàn)對 Spring Security 更多的自定義配置。
@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
配置密碼加密方式
必須配置,否則報空指針異常。
@Bean
PasswordEncoder passwordEncoder() {
// 不加密
return NoOpPasswordEncoder.getInstance();
}
Spring Security 提供了多種密碼加密方案,官方推薦使用 BCryptPasswordEncoder:
BCryptPasswordEncoder 使用 BCrypt 強哈希函數(shù),開發(fā)者在使用時可以選擇提供 strength 和 SecureRandom 實例。strength 取值在 4~31 之間(默認為 10)。strength 越大,密鑰的迭代次數(shù)越多(密鑰迭代次數(shù)為 2^strength)。如果是數(shù)據(jù)庫認證,庫里的密碼同樣也存放加密后的密碼。同一密碼每次 Bcrypt 生成的結(jié)果都會變化不會重復。
配置AuthenticationManagerBuilder 認證用戶、角色權(quán)限
支持直接配置內(nèi)存認證模式和配置UserDetailsServiceBean方式
內(nèi)存認證模式,實際項目不用這個哦。(僅做了解)
/**
* 配置用戶及其對應(yīng)的角色
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("ADMIN", "USER")
.and()
.withUser("laker").password("123").roles("USER");
}
上面在UserDetailsService的實現(xiàn)之一InMemoryUserDetailsManager中,即存儲在內(nèi)存中。
自定義UserDetailsServiceBean方式,實際項目都是使用這個,可定制化程度高。
步驟一:定義一個LakerUserService實現(xiàn)UserDetailsService,配置成SpringBean。該方法將在用戶登錄時自動調(diào)用。
@Service
public class LakerUserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// username 就是前端傳遞的例如 laker 123,即 laker
User user = userMapper.loadUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("賬戶不存在!");
}
user.setAuthorities(...);
return user;
}
}
username
password(加密后的密碼)
authorities
返回的Bean中以上3個都不能為空。返回User后由系統(tǒng)提供的 DaoAuthenticationProvider 類去比對密碼是否正確。
Spring Security默認支持表單請求登錄的源碼,UsernamePasswordAuthenticationFilter.java
步驟二:在把自定義的LakerUserService裝載進去.
@Autowired
UserService userService;
...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
步驟三:其中我們的業(yè)務(wù)用戶User必須要實現(xiàn)UserDetails接口,并實現(xiàn)該接口中的 7 個方法:
- getAuthorities():獲取當前用戶對象所具有的權(quán)限信息
- getPassword():獲取當前用戶對象的密碼
返回的密碼和用戶輸入的登錄密碼不匹配,會自動拋出 BadCredentialsException 異常。
- getUsername():獲取當前用戶對象的用戶名
- isAccountNonExpired():當前賬戶是否未過期
- isAccountNonLocked():當前賬戶是否未鎖定
- 返回了 false,會自動拋出 AccountExpiredException 異常。
- isCredentialsNonExpired():當前賬戶密碼是否未過期
- isEnabled():當前賬戶是否可用
@Data
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<String> authorities;
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return !locked;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authoritiesList = new ArrayList<>();
for (String authority : authorities) {
authoritiesList.add(new SimpleGrantedAuthority(authority));
}
return authoritiesList;
}
}
配置HttpSecurity Url訪問權(quán)限
/**
* 配置 URL 訪問權(quán)限
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//
http // 1.開啟 HttpSecurity 配置
.authorizeRequests()
// laker/** 模式URL必須具備laker.query
.antMatchers("/laker/**").hasAnyAuthority("laker.query")
// 用戶訪問其它URL都必須認證后訪問(登錄后訪問)
.anyRequest().authenticated()
.and()
// 2.開啟表單登錄,前后端分離的時候不用這個
.formLogin()
// 未登錄時 重定向的url 默認是/login 內(nèi)置的頁面,可以自己自定義哈。一般前后端分離,不用這個
// .loginPage("/login")
//
// .defaultSuccessUrl("/user",true)
// .usernameParameter("username") // default is username
// .passwordParameter("password") // default is password
// .loginPage("/authentication/login") // default is /login with an HTTP get
// .failureUrl("/authentication/login?failed") // default is /login?error
// .loginProcessingUrl("/authentication/login/process") // default is /login
.and()
// 3.關(guān)閉csrf,前后端分離不需要這個。
.csrf().disable();
//授權(quán)碼模式需要 會彈出默認自帶的登錄框
http.httpBasic();
// 開啟注銷登錄的配置
http.logout()
// 配置注銷登錄請求URL為"/logout"(默認也就是 /logout)
.logoutSuccessUrl("/logout")
.clearAuthentication(true) // 清除身份認證信息
.invalidateHttpSession(true) // 使 session 失效;
}
- formLogin() 表示開啟表單登錄
- **defaultSuccessUrl()**表示默認登錄驗證成功跳轉(zhuǎn)的url,默認重定向到上次訪問未成功的,如果沒有則重定向到
/. - loginProcessingUrl() 方法配置登錄接口為“/login”,即可以直接調(diào)用“/login”接口,發(fā)起一個 POST 請求進行登錄,登錄參數(shù)中用戶名必須為 username,密碼必須為 password,配置 loginProcessingUrl 接口主要是方便 Ajax 或者移動端調(diào)用登錄接口。
anyRequest | 匹配所有請求路徑
access | SpringEl表達式結(jié)果為true時可以訪問
anonymous | 匿名可以訪問 所有人都能訪問,但是帶上 token訪問后會報錯403
denyAll | 用戶不能訪問 所有人都能訪問,包括帶上 token 訪問
fullyAuthenticated | 用戶完全認證可以訪問(非remember-me下自動登錄)
hasAnyAuthority | 如果有參數(shù),參數(shù)表示權(quán)限,則其中任何一個權(quán)限可以訪問
hasAnyRole | 如果有參數(shù),參數(shù)表示角色,則其中任何一個角色可以訪問
hasAuthority | 如果有參數(shù),參數(shù)表示權(quán)限,則其權(quán)限可以訪問
hasIpAddress | 如果有參數(shù),參數(shù)表示IP地址,如果用戶IP和參數(shù)匹配,則可以訪問
hasRole | 如果有參數(shù),參數(shù)表示角色,則其角色可以訪問
permitAll | 用戶可以任意訪問
rememberMe | 允許通過remember-me登錄的用戶訪問
authenticated | 用戶登錄后可訪問
自定義successHandler
登錄成功后默認是重定向url,我們可以自定義返回json用于前后端分離場景以及其他邏輯,例如成功之后發(fā)短信等。
http.formLogin().successHandler((req, resp, authentication) -> {
// 發(fā)短信哈
Object principal = authentication.getPrincipal();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(principal));
out.flush();
out.close();
})自定義failureHandler
登錄失敗回調(diào)
http.formLogin().failureHandler((req, resp, e) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(e.getMessage());
out.flush();
out.close();
})
自定義未認證處理
http.exceptionHandling()
.authenticationEntryPoint((req, resp, authException) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write("尚未登錄,請先登錄");
out.flush();
out.close();
}
);
自定義權(quán)限不足處理
http.exceptionHandling()
//沒有權(quán)限,返回json
.accessDeniedHandler((request,response,ex) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = response.getWriter();
Map<String,Object> map = new HashMap<String,Object>();
map.put("code",403);
map.put("message", "權(quán)限不足");
out.write(objectMapper.writeValueAsString(map));
out.flush();
out.close();
})
自定義注銷登錄
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler((req, resp, authentication) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write("注銷成功");
out.flush();
out.close();
})
前后端分離場景
上面的都是入門的,實際項目中一般都是前后端分離的,在登錄時都是自定義登錄接口,例如登錄接口是restful風格,增加了其他的驗證碼參數(shù),還使用jwt來完成登錄鑒權(quán)等。
提供登錄接口
該接口需要在配置當中放行,未授權(quán)訪問需要授權(quán)的請求時,會返回401或者403狀態(tài)碼,前端可以根據(jù)這個進行路由提示處理。
@RestController
public class LoginController {
@Autowired
LoginService ...
@PostMapping("/login")
public login(@RequestBody Login login){
...
return token;
}
}
Service層創(chuàng)建UsernamePasswordAuthenticationToken對象,把用戶名和密碼封裝成Authentication對象.
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public doLogin(Login login) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password;
Authentication authenticate
try { // 該方法會去調(diào)用UserDetailsServiceImpl.loadUserByUsername
authenticate = authenticationManager.authenticate(authenticationToken);
} catch (AuthenticationException e) {
e.printStackTrace();
}
if (Objects.isNull(authenticate)) {
//用戶名密碼錯誤
throw new ServicesException(...);
}
User authUser = (User) authenticate.getPrincipal();
String token = JwtUtil.createJWT(username);
Map<String, String> map = new HashMap<>();
map.put("token", token);
return map;
}
}
自定義認證過濾器
坊間有2種實現(xiàn)方式。
方式一:繼承UsernamePasswordAuthenticationFilter的寫法需要使用登陸成功處理器、失敗處理器等,還是需要按照security這一套來玩。
Spring Security默認支持表單請求登錄的源碼UsernamePasswordAuthenticationFilter.java
方式二:使用Filter的寫法沒有任何限制怎么玩都行,比如說添加其他參數(shù)驗證碼,返回json,token鑒權(quán)等。
@Component
public class LakerOncePerRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (!StringUtils.isEmpty(token) )
{
// 校驗token ...
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, authorities;
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
}
} //3、在UsernamePasswordAuthenticationFilter前添加認證過濾器
http.addFilterBefore(lakerOncePerRequestFilter, UsernamePasswordAuthenticationFilter.class);
鑒權(quán)
1.注解鑒權(quán)
- 在
SpringSecurity配置類中開啟方法級的認證 - 使用
@PreAuthorize注解在方法或者類
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
@RestController
public class Controller {
@GetMapping("/hello")
@PreAuthorize("hasAnyAuthority('laker.query')")
public String test() {
}2.自定義Bean動態(tài)鑒權(quán)
因為@PreAuthorize支持SpringEL表達式,所以可以支持自定義SpringBean動態(tài)鑒權(quán)。
- 先自定義一個SpringBean。
- 使用
@PreAuthorize注解在方法或者類配合@PreAuthorize(“@rbacService.hasPermission(‘xx’)”)
@Component("rbacService")
public class LakerRBACService {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
UserDetails userDetails=(UserDetails)principal;
/**
* 該方法主要對比認證過的用戶是否具有請求URL的權(quán)限,有則返回true
*/
//本次要訪問的資源
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(request.getMethod() + "" + request.getRequestURI());
//用戶擁有的權(quán)限中是否包含請求的url
return userDetails.getAuthorities().contains(simpleGrantedAuthority);
}
return false;
}
public boolean hasPermission() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
/**
* 該方法主要對比認證過的用戶是否具有請求URL的權(quán)限,有則返回true
*/
//本次要訪問的資源
HttpServletRequest request =((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(request.getRequestURI());
//用戶擁有的權(quán)限中是否包含請求的url
return userDetails.getAuthorities().contains(simpleGrantedAuthority);
}
return false;
}
}
// controller方法
@PreAuthorize("@rbacService.hasPermission()")
public String test() {
}
// 或者高級的全局url鑒權(quán)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
http.authorizeRequests() //設(shè)置授權(quán)請求,任何請求都要經(jīng)過下面的權(quán)限表達式處理
.anyRequest().access("@rbacService.hasPermission(request,authentication)") //權(quán)限表達式
3.擴展默認方法自定義擴展根對象SecurityExpressionRoot
http://www.dhdzp.com/article/245172.htm
1.創(chuàng)建自定義根對象
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
/**
* 自定義表達式
* @param username 具有權(quán)限的用戶賬號
*/
public boolean hasUser(String... username) {
String name = this.getAuthentication().getName();
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String[] names = username;
for (String nameStr : names) {
if (name.equals(nameStr)) {
return true;
}
}
return false;
}
}
2.創(chuàng)建自定義處理器
創(chuàng)建自定義處理器,主要是重寫創(chuàng)建根對象的方法。
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
root.setDefaultRolePrefix(getDefaultRolePrefix());
return root;
}
}
3.配置GlobalMethodSecurityConfiguration
之前我們使用@EnableGlobalMethodSecurity開啟全局方法安全,而這些全局方法級別的安全配置就在GlobalMethodSecurityConfiguration配置類中。
可以擴展這個類來自定義默認值,但必須確保在類上指定@EnableGlobalMethodSecurity 注解,否則會bean沖突報錯。
@Configuration
// 將EnableGlobalMethodSecurity注解移到這里
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new CustomMethodSecurityExpressionHandler();
}
}4.controller使用自定義方法
@PreAuthorize("hasUser('laker','admin')")
public String test() {
...
}
登出
http.logout().logoutUrl("/logout").logoutSuccessHandler((request, response, authentication) -> {
// 刪除用戶token
...
// 返回json
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.write("OK");
out.flush();
out.close();
});
跨域
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();//允許跨域,配置后SpringSecurity會自動尋找name=corsConfigurationSource的Bean
http.csrf().disable();//關(guān)閉CSRF防御
}
@Configuration
public class CrosConfig {
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration cores=new CorsConfiguration();
cores.setAllowCredentials(true);//允許客戶端攜帶認證信息
//springBoot 2.4.1版本之后,不可以用 * 號設(shè)置允許的Origin,如果不降低版本,則在跨域設(shè)置時使用setAllowedOriginPatterns方法
// cores.setAllowedOrigins(Collections.singletonList("*"));//允許所有域名可以跨域訪問
cores.setAllowedOriginPatterns(Collections.singletonList("*"));
cores.setAllowedMethods(Arrays.asList("GET","POST","DELETE","PUT","UPDATE"));//允許哪些請求方式可以訪問
cores.setAllowedHeaders(Collections.singletonList("*"));//允許服務(wù)端訪問的客戶端請求頭
// 暴露哪些頭部信息(因為跨域訪問默認不能獲取全部頭部信息)
cores.addExposedHeader(jsonWebTokenUtil.getHeader());
// 注冊跨域配置
// 也可以使用CorsConfiguration 類的 applyPermitDefaultValues()方法使用默認配置
source.registerCorsConfiguration("/**",cores.applyPermitDefaultValues());
return source;
}
}
全局配置
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Autowired
TokenFilter tokenFilter;
/**
* 配置 URL 訪問權(quán)限
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//
http // 1.過濾請求
.authorizeRequests()
// 2.對于登錄login 驗證碼captcha 允許訪問
.antMatchers("/login").permitAll()
// 用戶訪問其它URL都必須認證后訪問(登錄后訪問)
.anyRequest().authenticated()
.and()
// 3.關(guān)閉csrf
.csrf().disable()
// 4.基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 5.頁面能不能以 frame、 iframe、 object 形式嵌套在其他站點中,用來避免點擊劫持(clickjacking)攻擊
.and().headers().frameOptions().disable();
// 異常處理
http.exceptionHandling()
// 未認證返回401
.authenticationEntryPoint((req, response, authException) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter out = response.getWriter();
out.write("尚未登錄,請先登錄");
out.flush();
out.close();
})
// 沒有權(quán)限,返回403 json
.accessDeniedHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = response.getWriter();
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 403);
map.put("message", "權(quán)限不足");
out.write(JSONUtil.toJsonPrettyStr(map));
out.flush();
out.close();
});
// 配置登出
http.logout().logoutUrl("/logout").logoutSuccessHandler((request, response, authentication) -> {
// 刪除用戶token
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.write("OK");
out.flush();
out.close();
});
// 添加JWT filter
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
}
/**
* 配置用戶及其對應(yīng)的角色
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
/**
* 適用于靜態(tài)資源的防攔截,css、js、image 等文件
* 配置的url不會保護它們免受CSRF、XSS、Clickjacking等的影響。
* 相反,如果您想保護端點免受常見漏洞的侵害,請參閱configure(HttpSecurity)
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**");
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 解決 無法直接注入 AuthenticationManager
*
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
}
參考:
https://blog.csdn.net/X_lsod/article/details/122914659
https://blog.csdn.net/godleaf/article/details/108318403
https://blog.csdn.net/qq_43437874/article/details/119543579
到此這篇關(guān)于SpringBoot Security從入門到實戰(zhàn)示例教程的文章就介紹到這了,更多相關(guān)SpringBoot Security入門內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringSecurity導致SpringBoot跨域失效的問題解決
- SpringBoot?SpringSecurity?JWT實現(xiàn)系統(tǒng)安全策略詳解
- SpringBoot整合Security權(quán)限控制登錄首頁
- SpringBoot?整合Security權(quán)限控制的初步配置
- SpringBoot?Security使用MySQL實現(xiàn)驗證與權(quán)限管理
- SpringBoot淺析安全管理之Spring Security配置
- SpringBoot+SpringSecurity+jwt實現(xiàn)驗證
- Springboot詳解整合SpringSecurity實現(xiàn)全過程
- SpringBoot Security實現(xiàn)單點登出并清除所有token
相關(guān)文章
Java中Spring MVC接收表單數(shù)據(jù)的常用方法
Spring MVC是Spring框架中的一個模塊,用于開發(fā)基于MVC(Model-View-Controller)架構(gòu)的Web應(yīng)用程序,它提供了一種輕量級的、靈活的方式來構(gòu)建Web應(yīng)用,同時提供了豐富的功能和特性,本文給大家介紹了Spring MVC接收表單數(shù)據(jù)的方法,需要的朋友可以參考下2024-05-05
SpringBoot+VUE實現(xiàn)前后端分離的實戰(zhàn)記錄
這篇文章主要介紹了SpringBoot+VUE實現(xiàn)前后端分離的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
java request.getHeader("user-agent")獲取瀏覽器信息的方法
這篇文章主要介紹了java request.getHeader("user-agent")獲取瀏覽器信息的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-03-03
kotlin java 混合代碼 maven 打包實現(xiàn)
這篇文章主要介紹了kotlin java 混合代碼 maven 打包實現(xiàn),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

