1. 程式人生 > >SpringSecurity(一)自定義登陸頁面與基於資料庫登陸

SpringSecurity(一)自定義登陸頁面與基於資料庫登陸

一、新建SpringBoot專案

在這裡插入圖片描述

二、修改配置檔案

資料庫結構
在這裡插入圖片描述

pom

<dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
		<dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- 解除thymeleaf對HTML語法的嚴查,不引入的話會有很多語法錯誤 --> <dependency> <groupId>net.sourceforge.
nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.21</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies>

我這裡的springboot版本為2.1.1.RELEASE

application.yml

spring.datasource.url=jdbc:mysql://localhost:3306/security?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123321
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

logging.level.com.springsecurity.dao=debug
spring.mvc.view.prefix=templates/
spring.mvc.view.suffix=.html

前端頁面
(1)public資料夾下的login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>登入</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css">
    <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"></script>
    <script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row" style="margin-top: 20px;">
        <div class="col-md-3">
            <h2>登陸</h2>
            <form action="mylogin" method="post">
                <div class="form-group">
                    <label for="userName">Username</label>
                    <input type="text" class="form-control" id="userName" name="username" placeholder="Enter username">
                </div>
                <div class="form-group">
                    <label for="password">Password:</label>
                    <input type="password" class="form-control" id="password" name="password" placeholder="Enter password">
                </div>


                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </div>
</div>

</body>
</html>

(2)模板引擎下的login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <title>登入</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css">
    <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"></script>
    <script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row" style="margin-top: 20px;">
        <div class="col-md-3">
            <h2>登陸</h2>
            <form th:action="@{/mylogin}" method="post">
                <div class="form-group">
                    <label for="userName">Username</label>
                    <input type="text" class="form-control" id="userName" name="username" placeholder="Enter username">
                </div>
                <div class="form-group">
                    <label for="password">Password:</label>
                    <input type="password" class="form-control" id="password" name="password" placeholder="Enter password">
                </div>

                <div class="form-group" th:if="${param.error}">
                    <p th:if="${session.SPRING_SECURITY_LAST_EXCEPTION}">
                    <p th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}"></p>
                    </p>
                </div>

                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </div>
</div>

</body>
</html>

SpringSecurityConfig

package com.springsecurity.config;

import com.springsecurity.service.MyUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.Md4PasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserService myUserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","/login.html","/testlogin","/mylogin") // 不需要登入就可以訪問
                .permitAll()
                .antMatchers("/user/**").hasAnyRole("USER") // 需要具有ROLE_USER角色才能訪問
                .antMatchers("/admin/**").hasAnyRole("ADMIN") // 需要具有ROLE_ADMIN角色才能訪問
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/testlogin") // 設定登入頁面
                .loginProcessingUrl("/mylogin")
                .defaultSuccessUrl("/index") // 設定預設登入成功後跳轉的頁面
        ;
    }

    // 密碼加密方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    // 重寫方法,自定義使用者
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserService);
    }
}

必要說明:
loginPage("/testlogin") 相當於一個普通請求。這裡則需要傳送到登陸頁面
loginProcessingUrl("/mylogin") 登陸頁面表單提交的action.必須為post請求
defaultSuccessUrl("/index") 登陸成功後,傳送這個請求

Controller接受處理請求

package com.springsecurity.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;

@Controller
public class UserController {

    @GetMapping("/testlogin")
    public String authenticationLogin() throws IOException {
        return "login";
    }

    @GetMapping("/")
    public String welcome(){
        return "redirect:/login.html";
    }

    @GetMapping("/index")
    @ResponseBody
    public String index() throws IOException {
        return "index html";
    }
}

UserDetailService

package com.springsecurity.service;

import com.springsecurity.bean.User;
import com.springsecurity.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

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

@Service
public class MyUserService implements UserDetailsService {
    @Autowired
    private UserDao userDao;

    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //從資料庫根據使用者名稱獲取使用者資訊
        System.out.println(s);
        User userByName = userDao.getUserByName(s);
        //建立一個新的UserDetails物件,最後驗證登陸的需要
        UserDetails userDetails=null;
        if(userByName!=null){
            System.out.println(userByName.getPassword());
            //建立一個集合來存放許可權
            Collection<GrantedAuthority> authorities = getAuthorities(userByName);
            //例項化UserDetails物件
            userDetails=new org.springframework.security.core.userdetails.User(s,userByName.getPassword(),true,true,true,true, authorities);
        }
        return userDetails;
    }

    private Collection<GrantedAuthority> getAuthorities(User user){
        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
        //注意:這裡每個許可權前面都要加ROLE_。否在最後驗證不會通過
        authList.add(new SimpleGrantedAuthority("ROLE_"+user.getRole()));
        return authList;
    }
}

Dao層

package com.springsecurity.dao;

import com.springsecurity.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserDao {

    @Select("SELECT * FROM user where username=#{s}")
    public User getUserByName(String s);
}

實體類JavaBean

package com.springsecurity.bean;

import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@ToString
public class User{

    private Integer id;
    private String username;
    private String password;
    private String role;

}

啟動boot專案,驗證登陸

在這裡插入圖片描述
在這裡插入圖片描述

在經過測試的時候,我發現直接訪問模板引擎下面的頁面會報404而在resources下建立public資料夾,裡面放html檔案就不會出404. 因為模板引擎相當於我們傳統的ssm專案裡面的WEB-INF資料夾。是受保護的。不能重定向以及直接訪問。 轉發沒問題。所以,兩個不同頁面下的請求傳送會有一點點差別。具體看上面程式碼。

form-login是spring security名稱空間配置登入相關資訊的標籤,它包含如下屬性: 
1.