Spring Security後臺登錄

1.創建項目

1. 導入相關依賴

在這裏插入圖片描述

除此之外還需要手動引入Druid連接池的依賴

<!--手動導入druid連接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.10</version>
    </dependency>
<build>
  <!--Mapper.xml和mapper在同一個目錄需要配置resources-->
    <resources>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
    </resources>
</build>    
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.8</version>
      <scope>provided</scope>
    </dependency>

2. 修改配置文件

spring:
  #配置數據庫相關信息
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    data-username: racs
    password: asfla123
    url: jdbc:mysql://rm-2ze4f9fp157q2msjkasf5o.mysql.rds.aliyuncs.com:3306/數據庫名稱?useUnicode=true&characterEncoding=utf8
    #解決字符編碼
  http:
    encoding:
      charset: utf-8
      force-request: true
      force-response: true
server:
  port: 8081
  tomcat:
    uri-encoding: UTF-8

3. 文件目錄結構
在這裏插入圖片描述
4. 編寫Member實體類

package org.magic.login.entiy;


import java.util.Collection;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * Member
 *
 * @author LQ
 * @Date 創建時間:2020-04-21 13:49:38
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member implements UserDetails {
  private static final long serialVersionUID = 1L;

//columns START

  /**
   * id
   */
  private String id;

  /**
   * 姓名
   */
  private String name;

  /**
   * 手機號碼
   */
  private String phone;

  /**
   * 住宅電話
   */
  private String telephone;

  /**
   * 聯繫地址
   */
  private String address;

  /**
   * enabled
   */
  private Boolean enabled;

  /**
   * 用戶名
   */
  private String username;

  /**
   * 密碼
   */
  private String password;

  /**
   * userface
   */
  private String userface;

  /**
   * remark
   */
  private String remark;

  public Collection<? extends GrantedAuthority> getAuthorities() {
    return null;
  }

  public boolean isAccountNonExpired() {
    return true;
  }

  public boolean isAccountNonLocked() {
    return true;
  }

  public boolean isCredentialsNonExpired() {
    return true;
  }

  public boolean isEnabled() {
    return true;
  }
}

5. 編寫響應實體類

package org.magic.login.entiy;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RespDataRv {

  /**
   * 狀態
   */
  private Integer status;

  /**
   * 信息
   */
  private String msg;

  /**
   * 對象
   */
  private Object obj;


  public static RespDataRv success(String msg) {
    return new RespDataRv(200, msg, null);
  }

  public static RespDataRv success(String msg, Object obj) {
    return new RespDataRv(200, msg, obj);
  }

  public static RespDataRv error(String msg) {
    return new RespDataRv(500, msg, null);
  }

  public static RespDataRv error(String msg, Object obj) {
    return new RespDataRv(500, msg, obj);
  }

}

6. 編寫SecurityConfig

package org.magic.login.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.magic.login.entiy.Member;
import org.magic.login.entiy.RespDataRv;
import org.magic.login.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private MemberService memberService;

  //編碼密碼
  @Bean
  PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(memberService);
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .usernameParameter("username")
        .passwordParameter("password")
        .loginProcessingUrl("/toLogin")
        //如果沒有登錄,則會跳轉到登錄頁面
        .loginPage("/login")
        .successHandler(new AuthenticationSuccessHandler() {


          /**
           * 如果登錄成功,則返回此信息
           *
           * @param req
           * @param resp
           * @param authentication
           * @throws IOException
           * @throws ServletException
           */
          public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter writer = resp.getWriter();
            Member member = (Member) authentication.getPrincipal();
            RespDataRv success = RespDataRv.success("登錄成功!", member);
            String str = new ObjectMapper().writeValueAsString(success);
            writer.write(str);
            writer.flush();
            writer.close();
          }
        }).failureHandler(new AuthenticationFailureHandler() {

      /**
       * 如果登錄失敗,則返回此信息
       *
       * @param req
       * @param resp
       * @param exception
       * @throws IOException
       * @throws ServletException
       */
      public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException {
        resp.setContentType("applicaiton/json;charset=utf-8");
        PrintWriter writer = resp.getWriter();
        RespDataRv respDataRv = RespDataRv.error("登錄失敗!");
        if (exception instanceof LockedException) {
          respDataRv.setMsg("賬戶被鎖定,請聯繫管理人員!");
        } else if (exception instanceof BadCredentialsException) {
          respDataRv.setMsg("用戶名或密碼輸入有誤,請重新輸入!");
        } else if (exception instanceof DisabledException) {
          respDataRv.setMsg("賬戶已過期,請聯繫管理員!");
        } else if (exception instanceof AuthenticationCredentialsNotFoundException) {
          respDataRv.setMsg("賬戶未進行身份認證,請聯繫管理員!");
        } else if (exception instanceof AccountStatusException) {
          respDataRv.setMsg("賬戶狀態異常,請聯繫管理員!");
        } else {
          respDataRv.setMsg("登錄發生未知錯誤,請聯繫管理員!");
        }
        writer.write(new ObjectMapper().writeValueAsString(respDataRv));
        writer.flush();
        writer.close();
      }
    }).permitAll()
        .and()
        .logout()
        .logoutSuccessHandler(new LogoutSuccessHandler() {


          /**
           * 註銷登錄,返回此信息
           *
           * @param req
           * @param resp
           * @param authentication
           * @throws IOException
           * @throws ServletException
           */
          public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter writer = resp.getWriter();
            RespDataRv success = RespDataRv.success("註銷成功!");
            writer.write(new ObjectMapper().writeValueAsString(success));
            writer.flush();
            writer.close();
          }
        }).permitAll()
        .and()
        .csrf()
        .disable();
  }
}

7. 編寫Controller

登錄後可以訪問的hello接口

package org.magic.login.controller;

import org.magic.login.entiy.RespDataRv;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

  @GetMapping("/hello")
  public Object hello() {
    return RespDataRv.success("hello world");
  }
}

登錄接口:

package org.magic.login.controller;

import org.magic.login.entiy.RespDataRv;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

  @GetMapping("/login")
  public Object login() {
    return RespDataRv.error("兄弟,你還沒有登陸!");
  }

}

8. 編寫Service

package org.magic.login.service;

import org.magic.login.entiy.Member;
import org.magic.login.mapper.MemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;

/**
 * @author liqiang
 */
@Service
public class MemberService implements UserDetailsService {

  @Autowired
  private MemberMapper memberMapper;

  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    Member member = memberMapper.loadUserByUsername(username);
    if (member == null) {
      throw new UsernameNotFoundException("用戶名不存在!");
    }
    return member;
  }
}


9. 編寫Mapper

package org.magic.login.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.magic.login.entiy.Member;
import org.springframework.stereotype.Component;

@Mapper
@Component
public interface MemberMapper {

  Member loadUserByUsername(String username);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.magic.login.mapper.MemberMapper">
  <resultMap id="Member" type="org.magic.login.entiy.Member">
    <result property="id" column="id" jdbcType="INTEGER"/>
    <result property="name" column="name" jdbcType="VARCHAR"/>
    <result property="phone" column="phone" jdbcType="CHAR"/>
    <result property="telephone" column="telephone" jdbcType="VARCHAR"/>
    <result property="address" column="address" jdbcType="VARCHAR"/>
    <result property="enabled" column="enabled" jdbcType="BIT"/>
    <result property="username" column="username" jdbcType="VARCHAR"/>
    <result property="password" column="password" jdbcType="VARCHAR"/>
    <result property="userface" column="userface" jdbcType="VARCHAR"/>
    <result property="remark" column="remark" jdbcType="VARCHAR"/>
  </resultMap>

	<!--自己的sql-->
	<select id="loadUserByUsername" resultMap="Member">
		select * from member o where userName=#{userName}
	</select>

</mapper>

2.使用Postman測試

1. 訪問一個需要處於登錄狀態的接口

在這裏插入圖片描述
2. 輸入賬號密碼登錄
在這裏插入圖片描述
3. 再次訪問hello接口
在這裏插入圖片描述
4. 註銷登錄
在這裏插入圖片描述
5. 再次訪問hello接口

在這裏插入圖片描述

3. 可能遇到的問題

  1. 在登錄測試的時候,老是顯示賬號被鎖定
    在這裏插入圖片描述
    這是我們自己處理的異常,將異常打印出來顯示如下:
    在這裏插入圖片描述
    原因是Member實體類在實現UserDetails的時候,重寫的4個方法最終return的是false,
    在這裏插入圖片描述
    false都改爲true就可以正常登錄了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章