├── src ├── main │ ├── resources │ │ ├── application.properties │ │ └── templates │ │ │ ├── loginpage.html │ │ │ └── welcome.html │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ ├── SpringSecurityAuthenicationApplication.java │ │ ├── ServletInitializer.java │ │ ├── repository │ │ └── UserDao.java │ │ ├── handle │ │ └── MyAccessDeniedHandler.java │ │ ├── controller │ │ └── HelloController.java │ │ ├── service │ │ └── UserDetialsServiceImpl.java │ │ └── config │ │ └── SecurityConfig.java └── test │ └── java │ └── com │ └── example │ └── demo │ └── SpringSecurityAuthenicationApplicationTests.java ├── SpringSecurity_Authenication.pdf ├── README.md └── pom.xml /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /SpringSecurity_Authenication.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincenttuan/2022-SpringSecurity-01-Authenication/HEAD/SpringSecurity_Authenication.pdf -------------------------------------------------------------------------------- /src/main/java/com/example/demo/SpringSecurityAuthenicationApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringSecurityAuthenicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringSecurityAuthenicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 5 | 6 | public class ServletInitializer extends SpringBootServletInitializer { 7 | 8 | @Override 9 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 10 | return application.sources(SpringSecurityAuthenicationApplication.class); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2022 SpringSecurity Authenication and authorization 2 | 2022 SpringSecurity-01 認證與授權篇 (直播教學) 3 |

4 | Java SDK版本:Java 8
5 | STS 開發工具:STS 4.7.1
6 |

7 | 8 |

上課 youtube 影音連結(錄影)

9 | https://www.youtube.com/watch?v=n2sH5-Vko1M&ab_channel=%E6%AE%B5%E7%B6%AD%E7%80%9A 10 | 11 |

上課 PDF 檔案

12 | https://github.com/vincenttuan/2022-SpringSecurity-01-Authenication/blob/main/SpringSecurity_Authenication.pdf 13 | 14 |

Thymeleaf 外掛:thymeleaf-extras-eclipse-plugin

15 |
16 | https://github.com/thymeleaf/thymeleaf-extras-eclipse-plugin 17 | 18 | 19 |

STS 歷史版本下載:

20 |
21 | https://github.com/spring-projects/sts4/wiki/Previous-Versions 22 | 23 | 24 |

Thymeleaf 宣告:

25 | <html xmlns:th="http://www.thymeleaf.org"
26 |       xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
27 | 
28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/repository/UserDao.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.repository; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | import org.springframework.stereotype.Repository; 7 | 8 | @Repository 9 | public class UserDao { 10 | public Map> users; 11 | { 12 | // A01, 1234, admin,normal,ROLE_manager 13 | Map info1 = new LinkedHashMap<>(); 14 | info1.put("password", "$2a$10$AVqlik75aZH7Ei0q5otyCeAv2ZbHAfiDjv2dopOOc1hSJWLi3QUou"); // 1234 15 | info1.put("authority", "admin,normal,ROLE_manager"); 16 | 17 | // A02, 5678, normal,ROLE_employee 18 | Map info2 = new LinkedHashMap<>(); 19 | info2.put("password", "$2a$10$lSkDD7IUtE71jCqllbr.GOz7pXuZzS.UQwwfgHUX43FyEDsheO6s6"); // 5678 20 | info2.put("authority", "normal,ROLE_employee"); 21 | 22 | users = new LinkedHashMap<>(); 23 | users.put("A01", info1); 24 | users.put("A02", info2); 25 | 26 | System.out.println(users); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/handle/MyAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.handle; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.springframework.security.access.AccessDeniedException; 10 | import org.springframework.security.web.access.AccessDeniedHandler; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class MyAccessDeniedHandler implements AccessDeniedHandler { 15 | 16 | @Override 17 | public void handle(HttpServletRequest request, HttpServletResponse response, 18 | AccessDeniedException accessDeniedException) throws IOException, ServletException { 19 | // 回應 403 狀態 20 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 21 | response.setCharacterEncoding("UTF-8"); 22 | response.setContentType("text/html;charset=UTF-8"); 23 | response.getWriter().write("權限不足!請聯絡管理員。"); 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/example/demo/SpringSecurityAuthenicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | class SpringSecurityAuthenicationApplicationTests { 7 | 8 | public static void main(String[] args) { 9 | 10 | PasswordEncoder pe = new BCryptPasswordEncoder(); 11 | String ecode = pe.encode("1234"); 12 | System.out.println(ecode); 13 | boolean matches = pe.matches("1234", ecode); 14 | System.out.println(matches); 15 | 16 | System.out.println(); 17 | 18 | String ecode2 = pe.encode("5678"); 19 | System.out.println(ecode2); 20 | boolean matches2 = pe.matches("5678", ecode2); 21 | System.out.println(matches2); 22 | 23 | // $2a$10$4HrYhe7nBj3aogISLLgRnux8t0V9CDWyCeM09J0AVt92fBbM6AUvm 24 | //boolean matches3 = pe.matches("1234", "$2a$10$4HrYhe7nBj3aogISLLgRnux8t0V9CDWyCeM09J0AVt92fBbM6AUvm"); 25 | //System.out.println(matches3); 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/templates/loginpage.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Login form 8 | 9 | 10 |
11 |
12 | Login form 13 | 14 |

15 | 16 |

17 | 18 |

19 | remember me 20 |

21 | 22 | 23 |

24 |
25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.ResponseBody; 6 | 7 | @Controller 8 | public class HelloController { 9 | 10 | @RequestMapping("/") 11 | public String welcome() { 12 | return "welcome"; 13 | } 14 | 15 | @RequestMapping("/loginpage") 16 | public String loginpage() { 17 | return "loginpage"; 18 | } 19 | 20 | @RequestMapping("/fail") 21 | @ResponseBody 22 | public String fail() { 23 | return "fail"; 24 | } 25 | 26 | @RequestMapping("/adminpage") 27 | @ResponseBody 28 | public String adminpage() { 29 | return "adminpage"; 30 | } 31 | 32 | @RequestMapping("/managerpage") 33 | @ResponseBody 34 | public String managerpage() { 35 | return "managerpage"; 36 | } 37 | 38 | @RequestMapping("/employeepage") 39 | @ResponseBody 40 | public String employeepage() { 41 | return "employeepage"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Welcome page 8 | 9 | 10 |

Welcome page

11 |
    12 |
  • 登入帳號(name):
  • 13 |
  • 登入帳號(principal.username):
  • 14 |
  • 憑證(credentials):
  • 15 |
  • 權限與角色(authorities):
  • 16 |
  • 客戶端地址(details.remoteAddress):
  • 17 |
  • Session Id(details.sessionId):
  • 18 |
19 |
Hello manager 您好!
20 |
Hello employee 您好!
21 | 22 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/service/UserDetialsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.service; 2 | 3 | import java.util.Map; 4 | import java.util.Map.Entry; 5 | import java.util.Optional; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.authority.AuthorityUtils; 10 | import org.springframework.security.core.userdetails.User; 11 | import org.springframework.security.core.userdetails.UserDetailsService; 12 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 13 | import org.springframework.stereotype.Service; 14 | 15 | import com.example.demo.repository.UserDao; 16 | 17 | @Service 18 | public class UserDetialsServiceImpl implements UserDetailsService { 19 | 20 | @Autowired 21 | private UserDao userDao; 22 | 23 | @Override 24 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 25 | System.out.println("loadUserByUsername: " + username); 26 | // 1. 查詢用戶是否存在 ? 27 | Optional>> opt= userDao.users 28 | .entrySet() 29 | .stream() 30 | .filter(e -> e.getKey().equals(username)) 31 | .findFirst(); 32 | if(!opt.isPresent()) throw new UsernameNotFoundException("Not found!"); 33 | 34 | // 2. 取得相關資料並進行密碼比對 35 | Map info = opt.get().getValue(); 36 | String password = info.get("password"); 37 | String authority = info.get("authority"); 38 | return new User(username, 39 | password, 40 | AuthorityUtils.commaSeparatedStringToAuthorityList(authority)); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.6.3 9 | 10 | 11 | com.example 12 | Spring-Security-Study 13 | 0.0.1-SNAPSHOT 14 | Spring-Security-Study 15 | Demo project for Spring Boot 16 | 17 | 1.8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-security 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-thymeleaf 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-thymeleaf 34 | 35 | 36 | 37 | org.thymeleaf.extras 38 | thymeleaf-extras-springsecurity5 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-web 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-test 49 | test 50 | 51 | 52 | 53 | org.springframework.security 54 | spring-security-test 55 | test 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-maven-plugin 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.demo.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 12 | 13 | import com.example.demo.handle.MyAccessDeniedHandler; 14 | 15 | // 若要自訂登入邏輯則要繼承 WebSecurityConfigurerAdapter 16 | @Configuration 17 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 18 | 19 | @Autowired 20 | private UserDetailsService userDetailsService; 21 | 22 | @Autowired 23 | private MyAccessDeniedHandler myAccessDeniedHandler; 24 | 25 | @Override 26 | protected void configure(HttpSecurity http) throws Exception { 27 | // 表單提交 28 | http.formLogin() 29 | // loginpage.html 表單 action 內容 30 | .loginProcessingUrl("/login") 31 | // 自定義登入頁面 32 | .loginPage("/loginpage") 33 | // 登入成功之後要造訪的頁面 34 | .successForwardUrl("/") // welcome 頁面 35 | // 登入失敗後要造訪的頁面 36 | .failureForwardUrl("/fail"); 37 | 38 | // 授權認證 39 | http.authorizeHttpRequests() 40 | // 不需要被認證的頁面:/loginpage 41 | .antMatchers("/loginpage").permitAll() 42 | // 權限判斷 43 | // 必須要有 admin 權限才可以訪問 44 | .antMatchers("/adminpage").hasAuthority("admin") 45 | // 必須要有 manager 角色才可以訪問 46 | .antMatchers("/managerpage").hasRole("manager") 47 | // 其他指定任意角色都可以訪問 48 | .antMatchers("/employeepage").hasAnyRole("manager", "employee") 49 | // 其他的都要被認證 50 | .anyRequest().authenticated(); 51 | 52 | // http.csrf().disable(); // 關閉 csrf 防護 53 | 54 | // 登出 55 | http.logout() 56 | .deleteCookies("JSESSIONID") 57 | .logoutSuccessUrl("/loginpage") 58 | .logoutRequestMatcher(new AntPathRequestMatcher("/logout")); // 可以使用任何的 HTTP 方法登出 59 | 60 | // 異常處理 61 | http.exceptionHandling() 62 | //.accessDeniedPage("/異常處理頁面"); // 請自行撰寫 63 | .accessDeniedHandler(myAccessDeniedHandler); 64 | 65 | // 勿忘我(remember-me) 66 | http.rememberMe() 67 | .userDetailsService(userDetailsService) 68 | .tokenValiditySeconds(60); // 通常都會大於 session timeout 的時間 69 | 70 | } 71 | 72 | // 注意!規定!要建立密碼演算的實例 73 | @Bean 74 | public PasswordEncoder getPasswordEncoder() { 75 | return new BCryptPasswordEncoder(); 76 | } 77 | 78 | } 79 | --------------------------------------------------------------------------------