SpringSecurity(一)自定義登陸頁面與基於資料庫登陸
阿新 • • 發佈:2019-01-12
一、新建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.