從零開始學Spring Boot系列-整合Spring Security實現使用者認證與授權

代码匠心發表於2024-06-29

在Web應用程式中,安全性是一個至關重要的方面。Spring Security是Spring框架的一個子專案,用於提供安全訪問控制的功能。透過整合Spring Security,我們可以輕鬆實現使用者認證、授權、加密、會話管理等安全功能。本篇文章將指導大家從零開始,在Spring Boot專案中整合Spring Security,並透過MyBatis-Plus從資料庫中獲取使用者資訊,實現使用者認證與授權。

環境準備

在開始之前,請確保你的開發環境已經安裝了Java、Gradle和IDE(如IntelliJ IDEA或Eclipse)。同時,你也需要在專案中引入Spring Boot、Spring Security、MyBatis-Plus以及資料庫的依賴。

建立Spring Boot專案

首先,我們需要建立一個Spring Boot專案。可以使用Spring Initializr(https://start.spring.io/)來快速生成專案結構。在生成專案時,選擇所需的依賴,如Web、Thymeleaf(或JSP)、Spring Security等。

新增Spring Security依賴

在專案的pom.xml(Maven)或build.gradle(Gradle)檔案中,新增Spring Security的依賴。
對於Gradle,新增以下依賴:

group = 'cn.daimajiangxin'
version = '0.0.1-SNAPSHOT'
description ='Spring Security'
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    runtimeOnly 'mysql:mysql-connector-java:8.0.17'
    // MyBatis-Plus 依賴
    implementation 'com.baomidou:mybatis-plus-spring-boot3-starter:3.5.5'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}

對於Maven,新增以下依賴:

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-security</artifactId>  
</dependency>

建立實體類

建立一個簡單的實體類,對映到資料庫表。

package cn.daimajiangxin.springboot.learning.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;

@TableName(value ="user")
@Data
public class User implements Serializable {
    /**
     * 學生ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 姓名
     */
    private String name;
    @TableField(value="user_name")
    private String userName;

    private String password;

    /**
     * 郵箱
     */
    private String email;

    /**
     * 年齡
     */
    private Integer age;

    /**
     * 備註
     */
    private String remark;



    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

建立Mapper介面

建立對應的Mapper介面,通常放在與實體類相同的包下,並繼承BaseMapper 介面。例如:

package cn.daimajiangxin.springboot.learning.mapper;

import cn.daimajiangxin.springboot.learning.model.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {

}

建立Mapper XML檔案

在resources的mapper目錄下建立對應的XML檔案,例如UserMapper.xml:

<?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="cn.daimajiangxin.springboot.learning.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="cn.daimajiangxin.springboot.learning.model.User">
            <id property="id" column="id" jdbcType="BIGINT"/>
            <result property="name" column="name" jdbcType="VARCHAR"/>
            <result property="user_name" column="userName" jdbcType="VARCHAR"/>
            <result property="password" column="password" jdbcType="VARCHAR"/>
            <result property="email" column="email" jdbcType="VARCHAR"/>
            <result property="age" column="age" jdbcType="INTEGER"/>
            <result property="remark" column="remark" jdbcType="VARCHAR"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,name,email,age,remark
    </sql>
  
    <select id="findAllUsers"  resultMap="BaseResultMap">
     select
       <include refid="Base_Column_List"></include>
     from user
    </select>
</mapper>

建立Service 介面

在service目錄下服務類介面UserService

package cn.daimajiangxin.springboot.learning.service;

import cn.daimajiangxin.springboot.learning.model.User;
import com.baomidou.mybatisplus.extension.service.IService;

public interface UserService extends IService<User> {

}

建立Service實現類

在service目錄下建立一個impl目錄,並建立UserService實現類UserServiceImpl

package cn.daimajiangxin.springboot.learning.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.daimajiangxin.springboot.learning.model.User;
import cn.daimajiangxin.springboot.learning.service.UserService;
import cn.daimajiangxin.springboot.learning.mapper.UserMapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{

}

建立UserDetailsService實現類

package cn.daimajiangxin.springboot.learning.service.impl;

import cn.daimajiangxin.springboot.learning.model.User;
import cn.daimajiangxin.springboot.learning.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.security.core.GrantedAuthority;
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;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Resource
    private  UserService userService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<User>  queryWrapper=new LambdaQueryWrapper<User>();
        queryWrapper.eq(User::getUserName,username);
        User user=userService.getOne(queryWrapper);
        List<GrantedAuthority> authorities = new ArrayList<>();
        return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(),authorities );
    }
}

Java配置類

建立一個配置類,並建立SecurityFilterChain 的Bean。

package cn.daimajiangxin.springboot.learning.config;

import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig   {
    @Resource
    private UserDetailsService userDetailsService;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorizeRequests ->
                        authorizeRequests
                                .requestMatchers("/", "/home")
                                .permitAll()
                                .anyRequest().authenticated() // 其他所有請求都需要認證
                )
                .formLogin(formLogin ->
                        formLogin
                                .loginPage("/login") // 指定登入頁面
                                .permitAll() // 允許所有人訪問登入頁面
                )
                .logout(logout ->
                        logout
                                .permitAll() // 允許所有人訪問登出URL
                )    // 註冊重寫後的UserDetailsService實現
                .userDetailsService(userDetailsService);
        return http.build();
        // 新增自定義過濾器或其他配置
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new StandardPasswordEncoder();
    }
}

建立登入頁面

在src/main/resources/templates目錄下建立一個Thymeleaf模板作為登入頁面,例如login.html。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>登入</title>
</head>
<body>
<form th:action="@{/doLogin}" method="post">
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="登入"/></div>
</form>
</body>
</html>

建立控制器

建立一個UserController,

package cn.daimajiangxin.springboot.learning.controller;

import cn.daimajiangxin.springboot.learning.model.User;
import cn.daimajiangxin.springboot.learning.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@RestController
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping({"/login",})
    public ModelAndView login(Model model) {
        ModelAndView mv = new ModelAndView("login");
        return mv ;
    }
    @PostMapping("/doLogin")
    public String doLogin(@RequestParam String username,@RequestParam String password) {
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("user_name",username);
        User user=  userService.getOne(queryWrapper);
        if(user==null){
            return "登入失敗,使用者沒有找到";
        }
        if(! user.getPassword().equals(password)){
            return "登入失敗,密碼錯誤";
        }
        return "登入成功";
    }

}

登入頁面

執行你的Spring Boot應用程式,用瀏覽器訪問http://localhost:8080/login.
20240601104136


和我一起學習更多精彩知識!!!關注我公眾號:程式碼匠心,實時獲取推送。

源文來自:https://daimajiangxin.cn

原始碼地址:https://gitee.com/daimajiangxin/springboot-learning

相關文章