spring boot 實(shí)例之 用戶登錄

轉(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)建用戶

用戶列表

創(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)證碼登錄:


手機(jī)號登錄

實(shí)際上這里用戶和密碼輸入的是手機(jī)號和驗(yàn)證碼(0000),前端可以控制髓废,根據(jù)不同的選擇改下提示文字巷懈。
點(diǎn)擊登錄后進(jìn)入hello頁面
hello頁面

可以點(diǎn)擊注銷,再試試用戶名密碼登錄慌洪。

總結(jié)

  1. 創(chuàng)建用戶認(rèn)證的信息對象顶燕;
  2. 引入spring security;
  3. spring security配置文件冈爹;
  4. 自定義token涌攻;
  5. 自定義provider;
  6. 登錄頁面
    當(dāng)前目錄結(jié)構(gòu):


    目錄結(jié)構(gòu)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犯助,一起剝皮案震驚了整個濱河市癣漆,隨后出現(xiàn)的幾起案子剂买,更是在濱河造成了極大的恐慌,老刑警劉巖癌蓖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬哼,死亡現(xiàn)場離奇詭異,居然都是意外死亡租副,警方通過查閱死者的電腦和手機(jī)坐慰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來用僧,“玉大人结胀,你說我怎么就攤上這事赞咙。” “怎么了糟港?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵攀操,是天一觀的道長。 經(jīng)常有香客問我秸抚,道長速和,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任剥汤,我火速辦了婚禮颠放,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吭敢。我一直安慰自己碰凶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布鹿驼。 她就那樣靜靜地躺著痒留,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蠢沿。 梳的紋絲不亂的頭發(fā)上伸头,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音舷蟀,去河邊找鬼恤磷。 笑死,一個胖子當(dāng)著我的面吹牛野宜,可吹牛的內(nèi)容都是我干的扫步。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼匈子,長吁一口氣:“原來是場噩夢啊……” “哼河胎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起虎敦,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤游岳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后其徙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胚迫,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年唾那,在試婚紗的時候發(fā)現(xiàn)自己被綠了访锻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖期犬,靈堂內(nèi)的尸體忽然破棺而出河哑,到底是詐尸還是另有隱情,我是刑警寧澤龟虎,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布璃谨,位于F島的核電站,受9級特大地震影響遣总,放射性物質(zhì)發(fā)生泄漏睬罗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一旭斥、第九天 我趴在偏房一處隱蔽的房頂上張望容达。 院中可真熱鬧,春花似錦垂券、人聲如沸花盐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽算芯。三九已至,卻和暖如春凳宙,著一層夾襖步出監(jiān)牢的瞬間熙揍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工氏涩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留届囚,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓是尖,卻偏偏與公主長得像意系,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子饺汹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345