轉(zhuǎn)載請注明出處spring boot 實(shí)例之 用戶登錄--碧波之心簡書
上兩篇完成了用戶信息表的增刪查乡数,接下來增加用戶登錄功能椭蹄。采用spring security來進(jìn)行權(quán)限控制。我們希望用戶可以通過用戶名+密碼净赴、郵箱+密碼绳矩、手機(jī)號+驗(yàn)證碼、微信登錄三種登錄途徑玖翅。
先用來完成用戶名+密碼或手機(jī)號+驗(yàn)證碼登錄翼馆。
前面做的用戶信息表不是用來登錄的,那些信息只是用戶的基本信息金度。為了在用戶列表請求得到的數(shù)據(jù)中不包含密碼写妥、手機(jī)號等敏感信息。把登錄用的數(shù)據(jù)分開保存审姓,關(guān)聯(lián)到用戶信息珍特。
建立對象
創(chuàng)建數(shù)據(jù)模型 Safety
package com.biboheart.demo.user.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Data
@Entity
@Table(name = "bh_user_safety")
public class Safety {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // ID
private Long uid; // 用戶ID
private String username; // 用戶名
private String mobile; // 手機(jī)號
private String password; // 密碼
private Long createTime; // 創(chuàng)建時間
private Long updateTime; // 最后修改時間
}
創(chuàng)建repository
package com.biboheart.demo.user.repository;
import com.biboheart.demo.user.basejpa.CustomRepository;
import com.biboheart.demo.user.domain.Safety;
public interface SafetyRepository extends CustomRepository<Safety, Long> {
Safety findByUsernameAndUidNot(String username, Long uid);
Safety findByMobileAndUidNot(String mobile, Long uid);
@Transactional
void deleteByUid(Long uid);
}
創(chuàng)建service
package com.biboheart.demo.user.service;
import com.biboheart.brick.exception.BhException;
import com.biboheart.demo.user.domain.Safety;
public interface SafetyService {
public Safety save(Safety safety) throws BhException;
public void delete(Long id, Long uid);
public Safety load(Long uid, String username, String mobile);
}
package com.biboheart.demo.user.service.impl;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.biboheart.brick.exception.BhException;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.brick.utils.TimeUtils;
import com.biboheart.demo.user.domain.Safety;
import com.biboheart.demo.user.repository.SafetyRepository;
import com.biboheart.demo.user.service.SafetyService;
@Service
public class SafetyServiceImpl implements SafetyService {
@Autowired
private SafetyRepository safetyRepository;
@Override
public Safety save(Safety safety) throws BhException {
if (null == safety.getId()) {
safety.setId(0L);
}
if (CheckUtils.isEmpty(safety.getUid())) {
throw new BhException("必須關(guān)聯(lián)到用戶");
}
if (CheckUtils.isEmpty(safety.getUsername())) {
if (!CheckUtils.isEmpty(safety.getMobile())) {
safety.setUsername(safety.getMobile());
}
}
if (CheckUtils.isEmpty(safety.getUsername())) {
throw new BhException("用戶名不能為空");
}
Safety source = safetyRepository.findByUid(safety.getUid());
if (null != source) {
safety.setId(source.getId());
}
source = safetyRepository.findByUsername(safety.getUsername());
if (null != source) {
if (!safety.getUid().equals(source.getUid())) {
throw new BhException("用戶名已經(jīng)被占用");
}
if (!source.getId().equals(safety.getId())) {
if (CheckUtils.isEmpty(safety.getId())) {
safety.setId(source.getId());
} else {
safetyRepository.deleteById(source.getId());
}
}
}
source = safetyRepository.findByMobile(safety.getMobile());
if (null != source) {
if (!safety.getUid().equals(source.getUid())) {
throw new BhException("用戶名已經(jīng)被占用");
}
if (!source.getId().equals(safety.getId())) {
if (CheckUtils.isEmpty(safety.getId())) {
safety.setId(source.getId());
} else {
safetyRepository.deleteById(source.getId());
}
}
}
if (!CheckUtils.isEmpty(safety.getPassword()) && safety.getPassword().length() != 32) {
safety.setPassword(DigestUtils.md5Hex(safety.getPassword()));
}
Long now = TimeUtils.getCurrentTimeInMillis();
if (CheckUtils.isEmpty(safety.getCreateTime())) {
safety.setCreateTime(now);
}
safety.setUpdateTime(now);
safety = safetyRepository.save(safety);
return safety;
}
@Override
public void delete(Long id, Long uid) {
if (!CheckUtils.isEmpty(uid)) {
safetyRepository.deleteByUid(uid);
}
if (!CheckUtils.isEmpty(id)) {
safetyRepository.deleteById(id);
}
}
@Override
public Safety load(Long uid, String username, String mobile) {
Safety safety = null;
if (!CheckUtils.isEmpty(uid)) {
safety = safetyRepository.findByUid(uid);
}
if (null == safety && !CheckUtils.isEmpty(username)) {
safety = safetyRepository.findByUsername(username);
}
if (null == safety && !CheckUtils.isEmpty(mobile)) {
safety = safetyRepository.findByMobile(mobile);
}
return safety;
}
}
創(chuàng)建控制器
這里我們先做一個保存的API,如果不保存一個密碼的話魔吐,登錄的功能不方便測試扎筒。先簡單的實(shí)現(xiàn)用戶登錄的功能,接下去再優(yōu)化酬姆,修改嗜桌。把能考慮到的不安全因素解決。
package com.biboheart.demo.user.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.biboheart.brick.exception.BhException;
import com.biboheart.brick.model.BhResponseResult;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.demo.user.domain.Safety;
import com.biboheart.demo.user.domain.User;
import com.biboheart.demo.user.service.SafetyService;
import com.biboheart.demo.user.service.UserService;
@RestController
public class SafetyController {
@Autowired
private UserService userService;
@Autowired
private SafetyService safetyService;
@RequestMapping(value = "/userapi/safety/save", method = {RequestMethod.POST})
public BhResponseResult<?> save(Safety safety) throws BhException {
User user = userService.load(safety.getUid());
if (null == user) {
// 如果不是對某個用戶設(shè)置登錄參數(shù)則創(chuàng)建一個新用戶辞色,用戶注冊時
user = new User();
String name = safety.getUsername();
if (CheckUtils.isEmpty(name)) {
name = safety.getMobile();
}
user.setName(name);
user = userService.save(user);
}
safety.setUid(user.getId());
safety = safetyService.save(safety);
return new BhResponseResult<>(0, "success", null != safety);
}
}
創(chuàng)建一個用戶
創(chuàng)建一個用戶名為biboheart骨宠,手機(jī)號為15000000000,密碼為test的用戶相满。用戶前面的列表接口能查到最新創(chuàng)建的用戶层亿。
開始登錄功能
引入spring security組件,開始開發(fā)用戶登錄功能立美。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
創(chuàng)建包:com.biboheart.demo.user.security匿又,用戶登錄功能都在這個包中完成。
創(chuàng)建security配置文件
package com.biboheart.demo.user.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.web.authentication.UsernamePasswordAuthenticationFilter;
import com.biboheart.demo.user.security.filter.BhAuthenticationFilter;
import com.biboheart.demo.user.security.provider.MobileCodeAuthenticationProvider;
import com.biboheart.demo.user.security.provider.UsernamePasswordAuthenticationProvider;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;
@Autowired
private MobileCodeAuthenticationProvider mobileCodeAuthenticationProvider;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(mobileCodeAuthenticationProvider)
.authenticationProvider(usernamePasswordAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf().disable()
.addFilterAt(bhAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/home", "/loginIn", "/oauth/authorize", "/oauth/token").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
// @formatter:on
}
@Bean
public BhAuthenticationFilter bhAuthenticationFilter() {
BhAuthenticationFilter filter = new BhAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
因?yàn)槲覀冇袃煞N登錄方式建蹄,所以我們建立usernamePasswordAuthenticationProvider和mobileCodeAuthenticationProvider兩個provider來處理登錄請求碌更。
UsernamePasswordAuthenticationToken是spring security內(nèi)置的token對象。我們就不再定義了洞慎,需要定義一個MobileCodeAuthenticationToken在包c(diǎn)om.biboheart.demo.user.security.tokens中
package com.biboheart.demo.user.security.tokens;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
public class MobileCodeAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
private String credentials;
public MobileCodeAuthenticationToken(Object principal, String credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
public MobileCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = null;
super.setAuthenticated(true); // must use super, as we override
}
// ~ Methods
// ========================================================================================================
public String getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
credentials = null;
}
}
創(chuàng)建過濾器
創(chuàng)建一個過濾器痛单,替換掉原來的UsernamePasswordAuthenticationFilter過濾器。定義為BhAuthenticationFilter 繼承至 AbstractAuthenticationProcessingFilter劲腿。
package com.biboheart.demo.user.security.filter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
import com.biboheart.demo.user.security.tokens.MobileCodeAuthenticationToken;
public class BhAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
// ~ Static fields/initializers
// =====================================================================================
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
public static final String SPRING_SECURITY_FORM_AUTYPE_KEY = "autype";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private String autypeParameter = SPRING_SECURITY_FORM_AUTYPE_KEY;
private boolean postOnly = true;
// ~ Constructors
// ===================================================================================================
public BhAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
String autype = obtainAutype(request);
System.out.println(this.getClass().getName() + "----autype:" + autype);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
if (autype == null) {
autype = "";
}
username = username.trim();
AbstractAuthenticationToken authRequest = null;
switch(autype) {
case "mobileCode":
authRequest = new MobileCodeAuthenticationToken(username, password);
break;
default:
authRequest = new UsernamePasswordAuthenticationToken(username, password);
break;
}
/*UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);*/
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected String obtainAutype(HttpServletRequest request) {
return request.getParameter(autypeParameter);
}
protected void setDetails(HttpServletRequest request,
AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setAutypeParameter(String autypeParameter) {
Assert.hasText(autypeParameter, "Autype parameter must not be empty or null");
this.autypeParameter = autypeParameter;
}
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
}
對于登錄請求旭绒,我們接收三個參數(shù)。分別是:username,password快压,autype圆仔。autype參數(shù)用于告知登錄類型:默認(rèn)為用戶名密碼方式,mobileCode為手機(jī)號驗(yàn)證碼方式蔫劣。如果是mobileCode方式坪郭,那么username對應(yīng)的是手機(jī)號,password對應(yīng)的是驗(yàn)證碼脉幢。
通過SecurityConfiguration中的.addFilterAt(bhAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)插入過濾器歪沃。
實(shí)現(xiàn)用戶認(rèn)證
用戶名密碼認(rèn)證的實(shí)現(xiàn)
package com.biboheart.demo.user.security.provider;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.demo.user.domain.Safety;
import com.biboheart.demo.user.domain.User;
import com.biboheart.demo.user.service.SafetyService;
import com.biboheart.demo.user.service.UserService;
@Component
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {
@Autowired
private SafetyService safetyService;
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
String password = (String) authentication.getCredentials();
if (CheckUtils.isEmpty(password)) {
throw new BadCredentialsException("密碼不能為空");
}
Safety safety = safetyService.load(null, username, null);
if (null == safety) {
throw new BadCredentialsException("用戶不存在");
}
User user = userService.load(safety.getUid());
if (null == user) {
throw new BadCredentialsException("用戶不存在");
}
if (password.length() != 32) {
password = DigestUtils.md5Hex(password);
}
if (!password.equals(safety.getPassword())) {
throw new BadCredentialsException("用戶名或密碼不正確");
}
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(username, password, listUserGrantedAuthorities(user.getId()));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
System.out.println(this.getClass().getName() + "---supports");
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(Long uid) {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
if (CheckUtils.isEmpty(uid)) {
return authorities;
}
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
}
手機(jī)號驗(yàn)證碼認(rèn)證的實(shí)現(xiàn)
package com.biboheart.demo.user.security.provider;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.demo.user.domain.Safety;
import com.biboheart.demo.user.domain.User;
import com.biboheart.demo.user.security.tokens.MobileCodeAuthenticationToken;
import com.biboheart.demo.user.service.SafetyService;
import com.biboheart.demo.user.service.UserService;
@Component
public class MobileCodeAuthenticationProvider implements AuthenticationProvider {
@Autowired
private SafetyService safetyService;
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String mobile = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
String code = (String) authentication.getCredentials();
if (CheckUtils.isEmpty(code)) {
throw new BadCredentialsException("驗(yàn)證碼不能為空");
}
Safety safety = safetyService.load(null, null, mobile);
if (null == safety) {
throw new BadCredentialsException("用戶不存在");
}
User user = userService.load(safety.getUid());
if (null == user) {
throw new BadCredentialsException("用戶不存在");
}
// 手機(jī)號驗(yàn)證碼業(yè)務(wù)還沒有開發(fā),先用4個0驗(yàn)證
if (!code.equals("0000")) {
throw new BadCredentialsException("驗(yàn)證碼不正確");
}
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(safety.getUsername(), code, listUserGrantedAuthorities(user.getId()));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
System.out.println(this.getClass().getName() + "---supports");
return (MobileCodeAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(Long uid) {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
if (CheckUtils.isEmpty(uid)) {
return authorities;
}
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
}
更多的認(rèn)證方式照著擴(kuò)展就可以了嫌松。
用戶登錄界面
用戶登錄需要界面沪曙,未登錄時要跳轉(zhuǎn)到登錄界面,登錄成功后要跳轉(zhuǎn)到首頁萎羔。
頁面跳轉(zhuǎn)控制器
package com.biboheart.demo.user.security.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class SecurityController {
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
return "login";
}
}
首頁控制器
package com.biboheart.demo.user.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HelloController {
@RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
public String homePage() {
return "index";
}
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return "hello";
}
}
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"></meta>
<title>Spring Security入門</title>
</head>
<body>
<h1>歡迎使用Spring Security!</h1>
<p>
點(diǎn)擊 <a href="/hello">這里</a> 打個招呼吧
</p>
</body>
</html>
hello.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<title>Hello World!</title>
</head>
<body>
<h1>Hello world!</h1>
<a href="/logout">注銷</a>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<title>登錄</title>
</head>
<body>
<div th:if="${param.containsKey('error')}">
<p class="error">登錄不成功液走,可能是用戶名/密碼錯了</p>
</div>
<div th:if="${param.containsKey('logout')}">
<h4>成功 </h4>
<p>您已經(jīng)成功退出</p>
</div>
<form method="post" role="form">
<div class="form-group">
用戶名密碼認(rèn)證 <input name="autype" type="radio" value="usernamePassword" checked="checked"/>
手機(jī)號驗(yàn)證碼 <input name="autype" type="radio" value="mobileCode"/>
</div>
<div class="form-group">
<label for="username">用戶名:</label>
<input type='text' name='username'/>
</div>
<div class="form-group">
<label for="password">密碼:</label>
<input type='password' name='password'/>
</div>
<!-- <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> -->
<button class="btn btn-primary" type="submit">登錄</button>
</form>
With Github: <a href="/login/github">click here</a>
</body>
</html>
在pom.xml中引入組件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
首頁是允許訪問的,點(diǎn)擊“這里”的鏈接贾陷。就進(jìn)入登錄頁面缘眶。
選擇手機(jī)號驗(yàn)證碼登錄:
實(shí)際上這里用戶和密碼輸入的是手機(jī)號和驗(yàn)證碼(0000),前端可以控制髓废,根據(jù)不同的選擇改下提示文字巷懈。
點(diǎn)擊登錄后進(jìn)入hello頁面
可以點(diǎn)擊注銷,再試試用戶名密碼登錄慌洪。
總結(jié)
- 創(chuàng)建用戶認(rèn)證的信息對象顶燕;
- 引入spring security;
- spring security配置文件冈爹;
- 自定義token涌攻;
- 自定義provider;
-
登錄頁面
當(dāng)前目錄結(jié)構(gòu):