├── 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 |
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 |
--------------------------------------------------------------------------------