官方文檔css
1、Spring Security介紹html
Spring Security是Spring Resource社區的一個安全組件,Spring Security爲JavaEE企業級開發提供了全面的安全防禦。Spring Security採用「安全層」的概念,使每一層都儘量安全,連續的安全層能夠達到全面的防禦。Spring Security能夠在Controller層、Service層、DAO層等以加註解的方式來保護應用程序的安全。Spring Security提供了細粒度的權限控制,能夠精細到每個API接口、每個業務的方法,或每個操做數據庫的DAO層的方法。Spring Security提供的是應用程序層的安全解決方案,一個系統的安全還須要考慮傳輸層和系統層的安全,如採用Https協議、服務器部署防火牆等。java
使用Spring Security的一個重要緣由是它對環境的無依賴性、低代碼耦合性。Spring Security提供了數十個安全模塊,模塊與模塊之間的耦合性低,模塊之間能夠自由組合來實現特定需求的安全功能。mysql
目標就是在這兩個領域。JavaEE有另外一個優秀的安全框架Apache Shiro,Apache Shiro在企業及的項目開發中十分受歡迎,通常使用在單體服務中。但在微服務架構中,目前版本的Apache Shiro是無能爲力的。另外一個選擇Spring Security的緣由,是Spring Security易應用於Spring Boot工程,也易於集成到採用Spring Cloud構建的微服務系統中。git
Spring Security提供了不少的安全驗證模塊並支持與不少技術的整合,在Spring Security框架中,主要包含了兩個依賴,分別是spring-security
-web
依賴和spring-security-config
依賴。github
2、使用案例web
新建一個Spring Boot工程,引入相關依賴算法
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency>
配置Spring Securityspring
新建WebSecurityConfig類,做爲配置類sql
@EnableWebSecurity@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder()); } @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("cralor").password(new BCryptPasswordEncoder().encode("123")).roles("USER").build()); manager.createUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123")).roles("ADMIN").build()); return manager; }
使用Spring Security須要對密碼加密,這裏使用BCryptPasswordEncoder。
spring security中的BCryptPasswordEncoder方法採用SHA-256 +隨機鹽+密鑰對密碼進行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着能夠解密(這個與編碼/解碼同樣),可是採用Hash處理,其過程是不可逆的。
(1)加密(encode):註冊用戶時,使用SHA-256+隨機鹽+密鑰把用戶輸入的密碼進行hash處理,獲得密碼的hash值,而後將其存入數據庫中。
(2)密碼匹配(matches):用戶登陸時,密碼匹配階段並無進行密碼解密(由於密碼通過Hash處理,是不可逆的),而是使用相同的算法把用戶輸入的密碼進行hash處理,獲得密碼的hash值,而後將其與從數據庫中查詢到的密碼hash值進行比較。若是二者相同,說明用戶輸入的密碼正確。
這正是爲何處理密碼時要用hash算法,而不用加密算法。由於這樣處理即便數據庫泄漏,黑客也很難破解密碼(破解密碼只能用彩虹表)。
InMemoryUserDetailsManager 類是將用戶信息存放在內存中,上述代碼會在內存中建立兩個用戶,cralor用戶具備「USER」角色,admin用戶具備「ADMIN」角色。
到目前爲止,咱們的WebSecurityConfig僅包含有關如何驗證用戶身份的信息。Spring Security如何知道咱們要求全部用戶進行身份驗證?Spring Security如何知道咱們想要支持基於表單的身份驗證?緣由是WebSecurityConfigurerAdapter
在configure(HttpSecurity http)
方法中提供了一個默認配置,咱們能夠自定義本身的配置。
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder()); } @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("cralor").password(new BCryptPasswordEncoder().encode("123")).roles("USER").build()); manager.createUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123")).roles("ADMIN").build()); return manager; } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() //以「/css/**」開頭的和「/index」資源不須要驗證,可直接訪問 .antMatchers("/css/**","/index").permitAll() //任何以「/db/」開頭的URL都要求用戶擁有「ROLE_USER」角色 .antMatchers("/user/**").hasRole("USER") //任何以「/db/」開頭的URL都要求用戶同時擁有「ROLE_ADMIN」和「ROLE_DBA」。因爲咱們使用的是hasRole表達式,所以咱們不須要指定「ROLE_」前綴。 .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") /* //確保對咱們的應用程序的任何請求都要求用戶進行身份驗證 .anyRequest().authenticated()*/ .and() //容許用戶使用基於表單的登陸進行身份驗證 .formLogin() //表單登錄地址「/login」,登陸失敗地址「/login-error」 .loginPage("/login").failureForwardUrl("/login-error") .and() .logout() //註銷地址 // .logoutUrl("/logout") //註銷成功,重定向到首頁 .logoutSuccessUrl("/") //指定一個自定義LogoutSuccessHandler。若是指定了,logoutSuccessUrl()則忽略。 //.logoutSuccessHandler(logoutHandler) //指定HttpSession在註銷時是否使其無效。默認true .invalidateHttpSession(true) //容許指定在註銷成功時刪除的cookie的名稱。這是CookieClearingLogoutHandler顯式添加的快捷方式。 .deleteCookies("name","ss","aa") .and() //異常處理會重定向到「/401」頁面 .exceptionHandling().accessDeniedPage("/401") // .httpBasic()//容許用戶使用HTTP基自己份驗證進行身份驗證 ; } }
在上述代碼中配置了相關的界面,如首頁、登錄頁,在Controller中作相關配置
@Controller public class MainController { @RequestMapping("/") public String root(){ return "redirect:/index"; } @RequestMapping("/index") public String index(){ return "index"; } @RequestMapping("user/index") public String userIndex(){ return "user/index"; } @RequestMapping("login") public String login(){ return "login"; } @RequestMapping("login-error") public String loginError(Model model){ model.addAttribute("loginError",true); return "login"; } /** * 退出登錄兩種方式,一種在配置類設置,一種在這裏寫就不須要配置了 * * 這裏 首先咱們在使用SecurityContextHolder.getContext().getAuthentication() 以前校驗該用戶是否已經被驗證過。 * 而後調用SecurityContextLogoutHandler().logout(request, response, auth) 來退出 * * logout 調用流程: * * 1 將 HTTP Session 做廢,解綁其綁定的全部對象。 * * 2 從SecurityContext移除Authentication 防止併發請求的問題。 * * 3 顯式地清楚當前線程的上下文裏的值。 * * 在應用的其餘地方再也不須要處理 退出。 * * 注意:你甚至都不須要在你的spring多添加任何配置(不論是基於註解仍是基於xml)。 * * @param request * @param response * @return */ /* @RequestMapping("logout") public String logoutPage (HttpServletRequest request, HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { new SecurityContextLogoutHandler().logout(request, response, auth); } return "redirect:/login"; }*/ @GetMapping("/401") public String accessDenied(){ return "401"; } }
編寫相關界面
在application.yml中配置thymeleaf引擎
spring: thymeleaf: mode: HTML5 encoding: UTF-8 cache: false
登錄界面login.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login page</title> <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}"/> </head> <body> <h1>Login page</h1> <p>User角色用戶: cralor / 123</p> <p>Admin角色用戶: admin / 123</p> <p th:if="${loginError}" class="error">用戶名或密碼錯誤</p> <form th:action="@{login}" method="post"> <label for="username">用戶名:</label> <input type="text" id="username" name="username" autofocus="autofocus"> <label for="password">密碼:</label> <input type="password" id="password" name="password"> <input type="submit" value="登錄"> </form> <a href="/index" th:href="@{/index}">返回首頁</a> </body> </html>
首頁index.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <head> <title>Hello Spring Security</title> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> </head> <body> <h1>Hello Spring Security</h1> <p>這個界面沒有受保護,你能夠進已被保護的界面.</p> <div th:fragment="logout" sec:authorize="isAuthenticated()"> 登陸用戶: <span sec:authentication="name"></span> | 用戶角色: <span sec:authentication="principal.authorities"></span> <div> <form action="#" th:action="@{/logout}" method="post"> <input type="submit" value="登出" /> </form> </div> </div> <ul> <li>點擊<a href="/user/index" th:href="@{/user/index}">去/user/index保護的界面</a></li> </ul> </body> </html>
權限不夠顯示的頁面401.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <body> <div > <div > <h2> 權限不夠</h2> </div> <div sec:authorize="isAuthenticated()"> <p>已有用戶登陸</p> <p>用戶: <span sec:authentication="name"></span></p> <p>角色: <span sec:authentication="principal.authorities"></span></p> </div> <div sec:authorize="isAnonymous()"> <p>未有用戶登陸</p> </div> <p> 拒絕訪問! </p> </div> </body> </html>
用戶首頁/user/index.html,被Spring Security保護,只有擁有「USER」角色的用戶才能訪問。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Hello Spring Security</title> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> </head> <body> <div th:substituteby="index::logout"></div> <h1>這個界面是被保護的界面</h1> <p><a href="/index" th:href="@{/index}">返回首頁</a></p> <p><a href="/blogs" th:href="@{/blogs}">管理博客</a></p> </body> </html>
啓動工程,在瀏覽器訪問localhost:8080,會被重定向到localhost:8080/index頁面。
點擊 「去/user/index保護的界面」 ,因爲「/user/index」界面須要「USER」權限,但尚未登錄,會被重定向到登錄界面「/login.html」。
使用具備「USER」權限的cralor用戶登錄,登錄成功後會被重定向到「localhost:8080/user/index」界面。
退出登錄,用admin用戶登錄,該用戶沒有「USER」權限,此時會返回權限不足界面。
修改WebSecurityConfig,給admin用戶加上「USER」角色。
@Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("cralor").password(new BCryptPasswordEncoder().encode("123")).roles("USER").build()); manager.createUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123")).roles("ADMIN","USER").build()); return manager; }
並再次訪問「localhost:8080/user/index」,可正常顯示。
Spring Security方法級保護
WebSecurityConfigEnableGlobalMethodSecurity
prePostEnabled:Spring Security的Pre和Post註解是否可用,即@PreAuthorize
和@PostAuthorize
是否可用;
secureEnabled:Spring Security的@Secured註解是否可用;
jsr250Enabled:Spring Security對JSP-250的註解是否可用。
通常只用到prePostEnabled。由於@PreAuthorize
註解和@PostAuthorize
註解更適合方法級安全控制,而且支持Spring EL表達式,適合Spring開發者。其中,@PreAuthorize
註解會在進入方法前進行權限驗證。@PostAuthorize
註解在方法執行後再進行權限驗證,此註解應用場景不多。如何使用方法級保護註解呢?例如:
有權限字符串ROLE_ADMIN
,在方法上能夠寫@PreAuthorize("harRole('ADMIN')")
,此處爲權限名,也能夠寫爲@PreAuthorize("harAuthority('ROLE_ADMIN')")
,驗證權限名和權限字符串兩者等價;
加多個權限,能夠寫爲@PreAuthorize("harRole('ADMIN','USER')")
,也可寫爲@PreAuthorize("harAuthority('ROLE_ADMIN','ROLE_USER')")
。
新添加一個API接口,在該接口上添加權限註解。寫一個Blog文章列表的API接口,只有管理員權限的用戶才能夠刪除Blog。
新建實體類Blog
public class Blog { private Long id; private String name; private String content; public Blog(){} public Blog(Long id, String name, String content) { this.id = id; this.name = name; this.content = content; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
新建IBlogService接口類(沒有DAO層操做數據庫,只是在內存中)。
public interface IBlogService { /** * 獲取全部Blog * @return */ List<Blog> getBlogs(); /** * 根據ID獲取Blog * @param id */ void deleteBlog(long id); }
實現類BlogServiceImpl,在構造方法添加兩個Blog對象。
@Service public class BlogServiceImpl implements IBlogService { private List<Blog> list=new ArrayList<>(); public BlogServiceImpl(){ list.add(new Blog(1L, " spring in action", "good!")); list.add(new Blog(2L,"spring boot in action", "nice!")); } @Override public List<Blog> getBlogs() { return list; } @Override public void deleteBlog(long id) { Iterator iter = list.iterator(); while(iter.hasNext()) { Blog blog= (Blog) iter.next(); if (blog.getId()==id){ iter.remove(); } } } }
BlogController寫兩個API接口,一個獲取全部Blog列表,第二個根據ID刪除Blog,須要有ADMIN權限。
@RestController @RequestMapping("/blogs") public class BlogController { @Autowired IBlogService blogService; @GetMapping public ModelAndView list(Model model) { List<Blog> list =blogService.getBlogs(); model.addAttribute("blogsList", list); return new ModelAndView("blogs/list", "blogModel", model); } /** * 須要擁有「ADMIN」角色權限才能夠訪問 * @param id 要刪除的Blog的id * @param model 返回的視圖模型 * @return 返回頁面 */ @PreAuthorize("hasAuthority('ROLE_ADMIN')") @GetMapping(value = "/{id}/deletion") public ModelAndView delete(@PathVariable("id") Long id, Model model) { blogService.deleteBlog(id); model.addAttribute("blogsList", blogService.getBlogs()); return new ModelAndView("blogs/list", "blogModel", model); } }
博客列表界面
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <body> 登陸用戶: <span sec:authentication="name"></span> | 用戶角色: <span sec:authentication="principal.authorities"></span> <br> <br> <div> <table > <thead> <tr> <td>博客編號</td> <td>博客名稱</td> <td>博客描述</td> </tr> </thead> <tbody> <tr th:each="blog: ${blogModel.blogsList}"> <td th:text="${blog.id}"></td> <td th:text="${blog.name}"></td> <td th:text="${blog.content}"></td> <td> <div > <a th:href="@{'/blogs/' + ${blog.id}+'/deletion'}"> 刪除 </a> </div> </td> </tr> </tbody> </table> </div> </body> </html>
啓動程序,使用admin用戶登錄,進入管理博客界面。
點擊刪除,刪除編號爲2的博客,刪除成功後如圖:
從新用cralor用戶登陸,點擊刪除時,會顯示權限不足。
可見,在方法級別上的安全驗證時經過相關的註解和配置來實現的。註解寫在Controller層仍是Service層都生效。
從數據庫讀取用戶的認證信息
數據庫MySql,ORM框架JPA。
添加MySql和JPA的依賴
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
配置數據庫的相關配置
spring: thymeleaf: mode: HTML5 encoding: UTF-8 cache: false datasource: url: jdbc:mysql://127.0.0.1:3306/spring-security?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8 username: root password: ok jpa: hibernate: ddl-auto: update show-sql: true
建立User實體類,使用JPA的@Entity註解,代表該Java對象會被映射到數據庫。id採用的生成策爲自增長,包含username和password兩個字段,其中authorities爲權限點的集合。
@Entity public class User implements UserDetails, Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column private String password; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private List<Role> authorities; public User() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } public void setAuthorities(List<Role> authorities) { this.authorities = authorities; } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
User類實現了UserDetails接口,該接口是實現Spring Security認證信息的核心接口。其中,getUsername()方法爲UserDetails接口的方法,這個方法不必定返回username,也能夠返回其餘信息,如手機號碼,郵箱地址等。getAuthorities()方法返回的是該用戶設置的權限信息,在本例中返回的是從數據庫讀取的該用戶的全部角色信息,權限信息也能夠是用戶的其餘信息。另外須要讀取密碼。最後幾個方法通常都返回true,可根據本身的需求進行業務判斷。
UserDetails接口源碼以下
public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
新建Role類,實現GrantedAuthority接口,重寫了getAuthority()方法。權
@Entity public class Role implements GrantedAuthority { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getAuthority() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } }
JpaRepository
public interface UserDao extends JpaRepository<User, Long> { User findByUsername(String username); }
UserDetailsService
@Service public class UserServiceImpl implements UserDetailsService { @Autowired private UserDao userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } }
最後修改Spring Security配置類WebSecurityConfig ,讓Spring Security從數據庫中獲取用戶的認證信息。
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /* @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder()); } @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("cralor").password(new BCryptPasswordEncoder().encode("123")).roles("USER").build()); manager.createUser(User.withUsername("admin").password(new BCryptPasswordEncoder().encode("123")).roles("ADMIN","USER").build()); return manager; }*/ //一、@Autowired和@Qualifier結合使用 // @Qualifier("userServiceImpl") // @Autowired //二、使用@Resource @Resource UserDetailsService userDetailsService; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); }
由於Spring Boot會自動配置一些Bean,上邊用到的UserDetailsService的bean已經在UserDetailsServiceAutoConfiguration類的inMemoryUserDetailsManager()方法自動裝配好了。此時單獨使用@Autowired註解自動注入UserDetailsService會報錯:
Could not autowire. There is more than one bean of 'UserDetailsService' type.
Beans:
inMemoryUserDetailsManager (UserDetailsServiceAutoConfiguration.class) userServiceImpl (UserServiceImpl.java)
能夠結合使用 @Qualifier("userServiceImpl")和 @Autowired(須要知道已經自動裝配好的Bean的名字),或者使用@Resource,建議使用@Resource。
在啓動程序前須要在數據庫中建庫、建表、初始化數據。建庫腳本以下:
CREATE DATABASE `spring-security` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
建表腳本以下:
/* Navicat Premium Data Transfer Source Server : root Source Server Type : MySQL Source Server Version : 50721 Source Host : localhost:3306 Source Schema : spring-security Target Server Type : MySQL Target Server Version : 50721 File Encoding : 65001 Date: 31/07/2018 17:36:26 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for role -- ---------------------------- DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of role -- ---------------------------- INSERT INTO `role` VALUES (1, 'ROLE_USER'); INSERT INTO `role` VALUES (2, 'ROLE_ADMIN'); -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'cralor', '$2a$10$E9Ex7Yp4JZlaslgXCkkl/ugt7hwfoT/tPRHjlYyC8JnsPeLE1Yry2'); INSERT INTO `user` VALUES (2, 'admin', '$2a$10$kcyDOQPHnpFEYYKuRTayYupejdw5HFUF6/zm0yT5jog/waRwc8THi'); -- ---------------------------- -- Table structure for user_role -- ---------------------------- DROP TABLE IF EXISTS `user_role`; CREATE TABLE `user_role` ( `user_id` bigint(20) NOT NULL, `role_id` bigint(20) NOT NULL, INDEX `user_id`(`user_id`) USING BTREE, INDEX `role_id`(`role_id`) USING BTREE, CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user_role -- ---------------------------- INSERT INTO `user_role` VALUES (1, 1); INSERT INTO `user_role` VALUES (2, 1); INSERT INTO `user_role` VALUES (2, 2); SET FOREIGN_KEY_CHECKS = 1;
由於Spring Security要求密碼必需加密,因此咱們直接存密碼」123「使用 BCryptPasswordEncoder 進行hash運算後的hash值。
新建一個測試類,對」123「進行hash運算。
@RunWith(SpringRunner.class) @SpringBootTest public class Springboot3ApplicationTests { @Test public void contextLoads() { BCryptPasswordEncoder util = new BCryptPasswordEncoder(); for(int i = 0 ; i < 10; i ++){ System. out.println("經hash後的密碼爲:"+util.encode("123" )); } } }
輸出結果以下,隨便複製兩個存到數據庫便可(具體原理請自行百度🙃)。
啓動程序,瀏覽器訪問http://localhost:8080,會發現跟以前存放在內存中的用戶信息的認證效果是同樣的。
本案例代碼地址:https://github.com/cralor7/springcloud/tree/master/chap13-security