繼續上一篇博客的項目,向下擴展
springsecurity中所有配置基本都來源於一個默認的WebSecurityConfigurerAdapter
所以我們要寫一個類來繼承它,放棄默認配置
叫SecurityConfig
添加一個login.html登錄頁面,登錄成功以後跳到之前寫的hello.html頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login page</title>
</head>
<body>
This is login page
<form action="/login" method="post">
username: <input name="username" /><br/>
password: <input name="password" /><br/>
<button type="submit">submit</button>
</form>
</body>
</html>
這個提交地址/login比較有意義,後面會用到配置
然後我們開始配置SecurityConfig
package com.security.securitydemo.security;
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.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @Author WQY
* @Date 2019/11/27 9:44
* @Version 1.0
*/
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
// 和login.html中表單提交的一直必須一樣,這樣才能讓springsecurity幫你處理請求
.loginProcessingUrl("/login")
.and()
.authorizeRequests()
//添加不需要過濾的地址
.antMatchers("/index", "/login", "/error").permitAll()
.anyRequest()
.authenticated();
}
}
然後啓動項目訪問http://localhost:8880
就會出現自定義的登錄界面
然後輸入用戶名密碼後會出現403
不急別慌,因爲開啓了debug所以能在控制檯看見走了一個CrsfFilter,這個filter需要一個參數,防止xss攻擊的,但是我們不需要,所以我們禁掉。
修改SecurityConfig。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
// 和login.html中表單提交的一直必須一樣,這樣才能讓springsecurity幫你處理請求
.loginProcessingUrl("/login")
.and()
.authorizeRequests()
.antMatchers("/index", "/login", "/error").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
在登陸就不會有403的錯誤了
但是會出現404的錯誤
因爲沒有設置登錄成功的頁面,所以http://localhost:8080作爲了默認頁面
然後我們在
SecurityConfig
中添加一個登錄成功頁面
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
// 和login.html中表單提交的一直必須一樣,這樣才能讓springsecurity幫你處理請求
.loginProcessingUrl("/login")
.successForwardUrl("/hello")
.and()
.authorizeRequests()
.antMatchers("/index", "/login", "/error").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
可是這樣我這會出現405
在修改SecurityConfig文件,添加默認成功頁面
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")//處理登錄請求接口
.successForwardUrl("/hello")
.defaultSuccessUrl("/hello",true)
.and()
.authorizeRequests()
.antMatchers("/index","/login","/error").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
這樣輸入賬號密碼後就進來了
自定義數據查詢
經過上面的配置,我們實現了自定義登錄頁,但是數據是寫死的
所以現在要從數據庫獲取用戶信息
比如我們的數據庫一共有兩張表,user表和role表
package com.security.securitydemo.entity;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
* @Description
* @Author WQY
* @Date 2019-11-05
*/
@Data
@Entity
@Table ( name ="user" )
public class UserEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id" )
private Long id;
/**
* 用戶名
*/
@Column(name = "username" )
private String username;
/**
* 密碼
*/
@Column(name = "password" )
private String password;
/**
* 保存時間
*/
@Column(name = "savetime" )
private Date savetime;
/**
* 手機號
*/
@Column(name = "iphone" )
private String iphone;
/**
* 權限
*/
@Column(name = "authority" )
private Long authority;
/**
* 性別,1男,2女
*/
@Column(name = "sex" )
private String sex;
/**
* 組織機構編號
*/
@Column(name = "organization_num" )
private String organizationNum;
/**
* 組織機構名稱
*/
@Column(name = "organization_name" )
private String organizationName;
}
package com.security.securitydemo.entity;
import lombok.Data;
import javax.persistence.*;
/**
* @Description
* @Author WQY
* @Date 2019-11-25
*/
@Data
@Entity
@Table ( name ="role" )
public class RoleEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id" )
private Long id;
@Column(name = "name" )
private String name;
}
並建立他們的Repository
package com.security.securitydemo.dao;
import com.security.securitydemo.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author WQY
* @date 2019/9/9 15:28
*/
public interface User_Jpa extends JpaRepository<UserEntity,Integer> {
UserEntity findByUsername(String username);
}
package com.security.securitydemo.dao;
import com.security.securitydemo.entity.RoleEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
/**
* @Author WQY
* @Date 2019/11/25 17:10
* @Version 1.0
*/
public interface Role_Jpa extends JpaRepository<RoleEntity,Long> {
@Query(value = "select d from RoleEntity d where d.id=?1")
RoleEntity selectById(Long id);
}
基礎項寫完以後就要與sprinsecurity結合了
springsecurity中對於用戶的表示有自己的實體類相對應,它就是UserDetails,所以我們編寫一個AuthUser實現這個接口:
package com.security.securitydemo.security.entity;
import com.security.securitydemo.entity.RoleEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author WQY
* @Date 2019/11/26 14:52
* @Version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthUser implements UserDetails {
private String username;
private String password;
private List<RoleEntity> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.isEmpty() ? Collections.EMPTY_LIST :
// ROLE_ 是springsecurity對於角色的默認前綴,如果不加,驗證會失敗
(roles.parallelStream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList())); }
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
其中關於賬號過期,enable全部設置爲通過,接下來就是編寫CustomUserDetailsService,返回我們自己的AuthUser:
package com.security.securitydemo.security.impl;
import com.security.securitydemo.dao.Role_Jpa;
import com.security.securitydemo.dao.User_Jpa;
import com.security.securitydemo.entity.UserEntity;
import com.security.securitydemo.security.entity.AuthUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
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 javax.annotation.Resource;
import java.util.Collections;
/**
* @Author WQY
* @Date 2019/11/26 14:55
* @Version 1.0
*/
@Service
@Primary
public class CustomUserDetailsServiceImpl implements UserDetailsService {
@Resource
private User_Jpa userRepository;
@Resource
private Role_Jpa roleRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("user: " + username + " is not found.");
}
return new AuthUser(user.getUsername(), user.getPassword(), roleRepository.findAllById(Collections.singletonList(user.getAuthority())));
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
System.out.println(bCryptPasswordEncoder.encode("123456"));
}
}
到此,其實我們的UserDetails已經注入了Spring中,其實我們加上@Service和@Primary之後,springsecurity就能自己檢測到這個bean,然後作爲自己的UserDetailsService。
因爲加了加密,所以先用main方法對123456加密,然後放入數據庫
最後是一個修改controller
package com.security.securitydemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author WQY
* @Date 2019/11/27 9:31
* @Version 1.0
*/
@Controller
@RequestMapping
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "/hello.html";
}
@RequestMapping("/login")
public String login() {
return "/login.html";
}
@RequestMapping("/index")
public String index() {
return "/index.html";
}
}
然後在訪問頁面輸入admin和123456(自己定義以後的)
就會進入hello.html頁面,由於在
SecurityConfig
中配置了幾個放行的頁面所以直接訪問http://localhost:8880/index也行
因爲他不需要權限
項目地址:https://download.csdn.net/download/qq_41755287/12000491