Spring Security 基本介紹
這里就不對Spring Security進行過多的介紹了,具體的可以參考官方文檔
我就只說下SpringSecurity核心功能:
- 認證(你是誰)
- 授權(你能干什么)
- 攻擊防護(防止偽造身份)
基本環境搭建
這里我們以SpringBoot作為項目的基本框架,我這里使用的是maven的方式來進行的包管理,所以這里先給出集成Spring Security的方式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
然后建立一個Web層請求接口
@Controller
public class IndexController {
/**
* 跳轉首頁
*/
@GetMapping("")
public void index1(HttpServletResponse response){
//內部重定向
try {
response.sendRedirect("/index");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 首頁
*/
@RequestMapping("/index")
@ResponseBody
public String index() {
return "index";
}
}
接下來可以直接進行項目的運行,并進行接口的調用看看效果了。
通過網頁的調用
我們首先通過瀏覽器進行接口的調用,直接訪問http://localhost:8080/index,如果接口能正常訪問,那么應該顯示“index”。
但是我們是沒法正常訪問的,出現了下圖的身份驗證輸入框
這是因為在SpringBoot中,默認的Spring Security就是生效了的,此時的接口都是被保護的,我們需要通過驗證才能正常的訪問。 Spring Security提供了一個默認的用戶,用戶名是user,而密碼則是啟動項目的時候自動生成的。
我們查看項目啟動的日志,會發現如下的一段Log
Using default security password: 62ccf9ca-9fbe-4993-8566-8468cc33c28c
當然你看到的password肯定和我是不一樣的,我們直接用user和啟動日志中的密碼進行登錄。
登錄成功后,就跳轉到了接口正常調用的頁面了。
如果不想一開始就使能Spring Security,可以在配置文件中做如下的配置:
# security 使能
security.basic.enabled = false
剛才看到的登錄框是SpringSecurity是框架自己提供的,被稱為httpBasicLogin。顯示它不是我們產品上想要的,我們前端一般是通過表單提交的方式進行用戶登錄驗證的,所以我們就需要自定義自己的認證邏輯了。
改造1 使用頁面表單登錄
- 前端寫一個登陸頁面(使用 thymeleaf 模板引擎),login_page.html文件:
<!DOCTYPE html>
<html id="ng-app" ng-app="app" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>home</title>
</head>
<body>
<form class="form-signin" action="/form" method="post">
<h2 class="form-signin-heading">用戶登錄</h2>
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="username" class="form-control" placeholder="請輸入用戶名"/></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password" class="form-control" placeholder="請輸入密碼" /></td>
</tr>
<tr>
<td colspan="2">
<button type="submit" class="btn btn-lg btn-primary btn-block" >登錄</button>
</td>
</tr>
</table>
</form>
</body>
</html>
寫一個controller方法指向該登陸頁面,不能使用@RestController
和@ResponseBody
,否則就返回字符串了。
@RequestMapping("/loginPage")
public String login() {
return "login_page";
}
還需要配置上:
spring:
# 定位模板的目錄,給返回的頁面添加后綴名
thymeleaf:
prefix: classpath:/templates/
suffix: .html
servlet:
content-type: text/html
mode: HTML5
- 添加一個類 SecurityConfig 繼承 WebSecurityConfigurerAdapter
重寫configure方法,并加上@Configuration 和@EnableWebSecurity 2個注解。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 關閉csrf防護
.csrf().disable()
.headers().frameOptions().disable()
.and();
http
//登錄處理
.formLogin() //表單方式,或httpBasic
.loginPage("/loginPage")
.loginProcessingUrl("/form")
.defaultSuccessUrl("/index") //成功登陸后跳轉頁面
.failureUrl("/loginError")
.permitAll()
.and();
http
.authorizeRequests() // 授權配置
//無需權限訪問
.antMatchers( "/css/**", "/error404").permitAll()
.antMatchers("/user/**").hasRole("USER")
//其他接口需要登錄后才能訪問
.anyRequest().authenticated()
.and();
}
}
與/ css / **和/ error404匹配的請求是完全可訪問的
與/ user / **匹配的請求要求用戶進行身份驗證,并且必須與USER角色相關聯
使用自定義登錄頁面和失敗URL啟用基于表單的身份驗證
loginPage("/login")表示登錄時跳轉的頁面,因為登錄頁面我們不需要登錄認證,所以我們需要添加 permitAll() 方法。
login-page 自定義登錄頁url,默認為/login
login-processing-url 登錄請求攔截的url,也就是form表單提交時指定的action
failureUrl=表示登錄出錯的頁面,我們可以簡單寫個提示:如 用戶名或密碼錯誤。
.csrf().disable() 說明:Spring Security4默認是開啟CSRF的,所以需要請求中包含CSRF的token信息,這里不添加這段代碼的話會出現異常,加上的話可以關閉csrf(關閉后有安全漏洞)。
測試:
1、輸入網址:http://127.0.0.1:8081/index,自動跳轉到:http://127.0.0.1:8081/loginPage,返回登陸頁面
2、輸入賬號密碼:錯誤的話返回http://127.0.0.1:8081/loginError,登陸失敗頁面
正確的話:返回http://127.0.0.1:8081/index,登陸成功頁面
改造2、自定義用戶名和密碼
1、用戶直接寫入內存中
很顯然,這樣改造之后,雖然登錄頁面是好看了,但還遠遠不能滿足我們的應用需求,所以第二步,我們改造自定義的用戶名和密碼。
自定義用戶名和密碼有2種方式,一種是在代碼中寫死,這也是官方的demo,另一種是使用數據庫
首先是第一種:如
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN")
.and()
.withUser("test").password(passwordEncoder().encode("test123")).roles("USER");
}
新的版本需要對密碼進行加密,不然會報錯,所以這里要使用passwordEncoder,程序運行起來,這時用我們自己的用戶名和密碼 輸入 admin 和123456 就可以了。
你也可以多幾個用戶,就多幾個withUser即可。
.and().withUser("test").password("test123").roles("ADMIN");
這樣我們就有了一個用戶名為test,密碼為test123的用戶了。
2、使用數據庫的用戶
第一種的只是讓我們體驗了一下Spring Security而已,我們接下來就要提供自定義的用戶認證機制及處理過程。
在講這個之前,我們需要知道spring security的原理,spring security的原理就是使用很多的攔截器對URL進行攔截,以此來管理登錄驗證和用戶權限驗證
。
用戶登陸,會被AuthenticationProcessingFilter攔截,調用AuthenticationManager的實現,而且AuthenticationManager會調用ProviderManager來獲取用戶驗證信息(不同的Provider調用的服務不同,因為這些信息可以是在數據庫上,可以是在LDAP服務器上,可以是xml配置文件上等),如果驗證通過后會將用戶的權限信息封裝一個User放到spring的全局緩存SecurityContextHolder中,以備后面訪問資源時使用。
所以我們要自定義用戶的校驗機制的話,我們只要實現自己的AuthenticationProvider就可以了。在用AuthenticationProvider 這個之前,我們需要提供一個獲取用戶信息的服務,實現 UserDetailsService 接口
用戶名密碼->(Authentication(未認證) -> AuthenticationManager ->AuthenticationProvider->UserDetailService->UserDetails->Authentication(已認證)
了解了這個原理之后,我們就開始寫代碼
UserDetails接口
UserDetailsService接口
實現 UserDetailsService接口 來返回User的對象實例
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private SysUserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//這里可以可以通過username(登錄時輸入的用戶名)然后到數據庫中找到對應的用戶信息,并構建成我們自己的UserInfo來返回。
SysUser sysUser = userService.findUserByName(username);
//由于權限參數不能為空,所以這里先使用AuthorityUtils.commaSeparatedStringToAuthorityList方法模擬一個admin的權限,該方法可以將逗號分隔的字符串轉換為權限集合。
//數據庫中的密碼是加密后的
return new User(username, sysUser.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
一般而言登錄的數據在protected void configure(AuthenticationManagerBuilder auth)中,所有需要修改SecurityConfig類里面的configure(AuthenticationManagerBuilder auth)方法
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//用戶認證處理
.userDetailsService(userDetailsService)
//密碼處理
.passwordEncoder(passwordEncoder());
}
重新運行項目,這時候就可以使用數據庫的賬號和密碼登陸了。
一般而言到這里就可以實現登陸驗證了。但是遇到一個問題,這樣的話用戶名和密碼都是定死的,我們拿不到form-data數據,如果因為前端的問題,這種密碼登錄方式以外,我們還要稍微修改提交給我們的form-data中的密碼數據,做一下處理,自定義一個登錄呢。這個時候就需要用到AuthenticationProvider了。
AuthenticationProvider接口
這是一個接口,提供了兩種方法
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
通過第一個方法我們可以拿到form-data的數據,并且返回一個UserDetails如果登錄成功的話,或者返回null如果登錄失敗。
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = (String) authentication.getPrincipal(); //拿到username
String password = (String) authentication.getCredentials(); //拿到password
UserDetails userDetails = studentService.loadUserByUsername(userName);
if (/*自定義的驗證通過*/) {
return new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
}
/*驗證不通過*/
return null;
第二個方法是告訴spring sec我們這個驗證支持哪種驗證。
auth.userDetailsService(studentService).passwordEncoder(encoder);
這種驗證屬于Dao
驗證。
還有UsernamePasswordAuthentication
驗證。
我們的是UsernamePassword的驗證方式,所以在第二個方法中一般會這么寫
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
新建類 MyAuthenticationProvider 繼承AuthenticationProvider
完整的代碼如下:
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
/**
* 注入我們自己定義的用戶信息獲取對象
*/
@Autowired
private MyUserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// TODO Auto-generated method stub
String userName = authentication.getName();// 這個獲取表單輸入中返回的用戶名;
String password = (String) authentication.getCredentials();// 這個是表單中輸入的密碼;
// 這里構建來判斷用戶是否存在和密碼是否正確
UserDetails userInfo = userDetailsService.loadUserByUsername(userName); // 這里調用我們的自己寫的獲取用戶的方法;
if (userInfo == null) {
throw new BadCredentialsException("用戶名不存在");
}
boolean flag = passwordEncoder.matches(password,userInfo.getPassword());
if (!flag) {
throw new BadCredentialsException("密碼不正確");
}
Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
// 構建返回的用戶登錄成功的token
return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
//return new UsernamePasswordAuthenticationToken(userInfo, null,authorities);
}
@Override
public boolean supports(Class<?> authentication) {
// 這里直接改成retrun true;表示是支持這個執行
return true;
}
}
到此為止,我們的用戶信息的獲取,校驗部分已經完成了。接下來要讓它起作用,則我們需要在配置文件中修改,讓他起作用。回到我的SecurityConfig代碼文件,修改如下:
1、注入我們自己的AuthenticationProvider
2、修改配置的方法:
@Autowired
private AuthenticationProvider provider; //注入我們自己的AuthenticationProvider
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
現在重新運行程序,則需要輸入用戶名為 admin 密碼是123456之后,才能正常登錄了。
為了方便測試,我們調整添加另一個控制器 /whoim 的代碼 ,讓他返回當前登錄的用戶信息,前面說了,他是存在SecurityContextHolder 的全局變量中,所以我們可以這樣獲取
@RequestMapping("/whoim")
@ResponseBody
public Object whoIm()
{
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
我們運行,直接反問 /whoim ,則直接跳轉到登錄頁面,我們驗證過之后,再訪問此url,結果如下:
改造3、自定義登錄成功和失敗的處理邏輯
在現在的大多數應用中,一般都是前后端分離的,所以我們登錄成功或失敗都需要用json格式返回,或者登錄成功之后,跳轉到某個具體的頁面。
接下來我們來實現這種改造。
為了實現這個功能,只要實現AuthenticationSuccessHandler
接口的onAuthenticationSuccess
方法即可。
處理登錄成功的:
@Component
public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//response.setContentType("application/json;charset=utf-8");
//response.getWriter().write(objectMapper.writeValueAsString(authentication));
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null){
redirectStrategy.sendRedirect(request, response, "/index");
}else {
System.out.println(savedRequest.getRedirectUrl());
redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());
}
}
}
和自定義登錄成功處理邏輯類似,自定義登錄失敗處理邏輯需要實現AuthenticationFailureHandler
的onAuthenticationFailure
方法:
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper mapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));
}
}
代碼完成之后,修改配置config類代碼。
添加2個注解,自動注入
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler myAuthenticationFailHander;
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
//super.configure(http);
http
.formLogin().loginPage("/login").loginProcessingUrl("/login/form")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHander)
.permitAll() //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.csrf().disable();
}
進行測試,成功返回json格式的(登錄成功和失敗的)
改造4、添加權限控制
之前的代碼我們用戶的權限沒有加以利用,現在我們添加權限的用法。
之前的登錄驗證通俗的說,就是來判斷你是誰(認證),
而權限控制就是用來確定:你能做什么或者不能做什么(權限)
在講這個之前,我們簡單說下,對于一些資源不需要權限認證的,那么就可以在Config中添加 過濾條件,如:
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
//super.configure(http);
http
.formLogin().loginPage("/login").loginProcessingUrl("/login/form")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHander)
.permitAll() //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
.and()
.authorizeRequests()
.antMatchers("/index").permitAll() //這就表示 /index這個頁面不需要權限認證,所有人都可以訪問
.anyRequest().authenticated() //其他的需要做權限認證
.and()
.csrf().disable();
}
那么我們直接訪問 /index 就不會跳轉到登錄頁面,這樣我們就可以把一些不需要驗證的資源以這種方式過濾,比如圖片,腳本,樣式文件之類的。
我們先來看第一種權限控制:在編碼中寫死的。
其實權限控制也是通過這種方式來實現:
http
.formLogin().loginPage("/login").loginProcessingUrl("/login/form")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHander)
.permitAll() //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
.and()
.authorizeRequests()
.antMatchers("/index").permitAll()
.antMatchers("/whoim").hasRole("ADMIN") //這就表示/whoim的這個資源需要有ROLE_ADMIN的這個角色才能訪問。不然就會提示拒絕訪問
.anyRequest().authenticated() //必須經過認證以后才能訪問
.and()
.csrf().disable();
這個用戶的角色哪里來,就是我們自己的UserDetailsService中返回的用戶信息中的角色權限信息,
這里需要注意一下就是 .hasRole("ADMIN"),那么給用戶的角色時就要用:ROLE_ADMIN
.antMatchers 這里也可以限定HttpMethod的不同要求不同的權限(適用于Restful風格的API).
如:Post需要 管理員權限,get 需要user權限,我們可以這么個改造,同時也可以通過通配符來是實現 如:/user/1 這種帶參數的URL
.antMatchers("/whoim").hasRole("ADMIN")
.antMatchers(HttpMethod.POST,"/user/").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/user/").hasRole("USER")
Spring Security 的校驗的原理:左手配置信息,右手登錄后的用戶信息,中間投票器。
從我們的配置信息中獲取相關的URL和需要的權限信息,然后獲得登錄后的用戶信息,
然后經過:AccessDecisionManager 來驗證,這里面有多個投票器:AccessDecisionVoter,(默認有幾種實現:比如:1票否決(只要有一個不同意,就沒有權限),全票通過,才算通過;只要有1個通過,就全部通過。類似這種的。
WebExpressionVoter 是Spring Security默認提供的的web開發的投票器。(表達式的投票器)
Spring Security 默認的是 AffirmativeBased 只要有一個通過,就通過。
有興趣的可以 從FilterSecurityInterceptor這個過濾器入口,來查看這個流程。
內嵌的表達式有:permitAll denyAll 等等。
每一個權限表達式都對應一個方法。
如果需要同時滿足多個要求的,不能連寫如 ,我們有個URL需要管理員權限也同時要限定IP的話,不能:.hasRole("ADMIN").hasIPAddress("192.168.1.1");
而是需要用access方法 .access("hasRole('ADMIN') and hasIpAddress('192.168.1.1')");這種。
那我們可以自己寫權限表達式嗎? 可以,稍后。。。這些都是硬編碼的實現,都是在代碼中寫入的,這樣的靈活性不夠。所以我們接下來繼續改造
改造4、添加基于RBAC(role-Based-access control)權限控制
這個大家可以去百度一下,一般都是由 3個部分組成,一個是用戶,一個是角色 ,一個是資源(菜單,按鈕),然后就是 用戶和角色的關聯表,角色和資源的關聯表
核心就是判斷當前的用戶所擁有的URL是否和當前訪問的URL是否匹配。
首先我們自己提供一個判斷的接口和實現,代碼如下:
/**
* 返回權限驗證的接口
*/
public interface RbacService {
boolean hasPermission(HttpServletRequest request,Authentication authentication);
}
@Component("rbacService")
public class RbacServiceImpl implements RbacService {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
boolean hasPermission = false;
if (principal instanceof UserDetails) { //首先判斷先當前用戶是否是我們UserDetails對象。
String userName = ((UserDetails) principal).getUsername();
Set<String> urls = new HashSet<>(); // 數據庫讀取 //讀取用戶所擁有權限的所有URL
urls.add("/whoim");
// 注意這里不能用equal來判斷,因為有些URL是有參數的,所以要用AntPathMatcher來比較
for (String url : urls) {
if (antPathMatcher.match(url, request.getRequestURI())) {
hasPermission = true;
break;
}
}
}
return hasPermission;
}
}
然后在Security的配置項中添加自定義的權限表達式就可以了。
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
//super.configure(http);
http
.formLogin().loginPage("/login").loginProcessingUrl("/login/form")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHander)
.permitAll() //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
.and()
.authorizeRequests()
// .antMatchers("/index").permitAll()
// .antMatchers("/whoim").hasRole("ADMIN")
// .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
// .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
.anyRequest().access("@rbacService.hasPermission(request,authentication)") //必須經過認證以后才能訪問
.and()
.csrf().disable();
}
其中 @rbacService 就是我們自己聲明的bean,在RbacServiceImpl實現類的頭部注解中。
改造5、記住我的功能Remeber me
本質是通過token來讀取用戶信息,所以服務端需要存儲下token信息
根據官方的文檔,token可以通過數據庫存儲 數據庫腳本
CREATE TABLE persistent_logins (
username VARCHAR(64) NOT NULL,
series VARCHAR(64) NOT NULL,
token VARCHAR(64) NOT NULL,
last_used TIMESTAMP NOT NULL,
PRIMARY KEY (series)
);
然后,配置好token 的存儲 及數據源
@Autowired
private DataSource dataSource; //是在application.properites
/**
* 記住我功能的token存取器配置
* @return
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
修改Security配置
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
//super.configure(http);
http
.formLogin().loginPage("/login").loginProcessingUrl("/login/form")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHander)
.permitAll() //表單登錄,permitAll()表示這個不需要驗證 登錄頁面,登錄失敗頁面
.and()
.rememberMe()
.rememberMeParameter("remember-me").userDetailsService(userDetailsService)
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60)
.and()
.authorizeRequests()
// .antMatchers("/index").permitAll()
// .antMatchers("/whoim").hasRole("ADMIN")
// .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
// .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
.anyRequest().access("@rbacService.hasPermission(request,authentication)") //必須經過認證以后才能訪問
.and()
.csrf().disable();
在登陸頁面login.html上還要加上記住密碼的勾選框
<tr>
<td colspan="2"><input type="checkbox" name="remember-me" value="true"/>記住我</td>
</tr>
登錄之后 數據庫就會有一條數據
然后,服務重新啟動下,我們在看下直接訪問 /whoim 的話,就可以直接訪問了,不需要再登錄了。
到此為止我們的Spring Securtiy 的基本用法已經改造完成了。
源碼鏈接:https://github.com/visionsws/vicente-demo
參考文章
Spring boot 中 Spring Security 使用改造5部曲
SpringBoot + Spring Security 基本使用及個性化登錄配置
Hello Spring Security with Boot