1、 工於成其實,必先搭建springboot工程,配置我們pom.xml 所需的jar依賴
<!-- thymeleaf 模板依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- springSecurity 权限控制依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- thymeleaf-extras-springsecurity4 該jar必須手動引入 --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.2.RELEASE</version><!--$NO-MVN-MAN-VER$--> </dependency> <!-- mysql數據庫 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 引入 jpa.jar --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency> |
2、對我們 Application.yml 配置,整個項目環境
spring: thymeleaf: encoding: UTF-8 cache: false #热部署静态文件,禁止缓存 mode: HTML5 #使用HTML5标准 #配置数据源 datasource: username: root password: '123456' url: jdbc:mysql://127.0.0.1:3306/springsecurityStudying?characterEncoding=utf-8&useSSL=false driver-class-name: com.mysql.jdbc.Driver jpa: hibernate: ddl-auto: update #自动更新建表 show-sql: true database-platform: org.hibernate.dialect.MySQLDialect # 配置數據庫方言 |
3、開始編寫我們的所需的SQL脚本
SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `authority`; CREATE TABLE `authority` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `auth_Name` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; INSERT INTO `authority` VALUES ('1', 'ADMIN'); INSERT INTO `authority` VALUES ('2', 'MANAGER'); INSERT INTO `authority` VALUES ('3', 'PRESIDENT'); DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_name` varchar(32) NOT NULL, `pass_word` varchar(99) NOT NULL, `email` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `user_name` (`user_name`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; INSERT INTO `user` VALUES ('1', 'baihoo', '$2a$10$N7ME1n6kScoF3NkNaICqFuD2anQpOanTxDiWXIF9qjsgtWnCTXLsi', '[email protected]'); INSERT INTO `user` VALUES ('2', 'baihoo.god', '$2a$10$N7ME1n6kScoF3NkNaICqFuD2anQpOanTxDiWXIF9qjsgtWnCTXLsi', '[email protected]'); INSERT INTO `user` VALUES ('3', 'baihoo.chen', '$2a$10$N7ME1n6kScoF3NkNaICqFuD2anQpOanTxDiWXIF9qjsgtWnCTXLsi', '[email protected]'); INSERT INTO `user` VALUES ('5', 'baiHoo2', '$2a$10$pPreldvox9oX/haR.ugUjuZi8nmLXjcECfEX6BPFPxbIPTvGxAsjG', '[email protected]'); DROP TABLE IF EXISTS `user_authority`; CREATE TABLE `user_authority` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) DEFAULT NULL, `authority_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `authority_id` (`authority_id`), CONSTRAINT `user_authority_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `user_authority_ibfk_2` FOREIGN KEY (`authority_id`) REFERENCES `authority` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; INSERT INTO `user_authority` VALUES ('1', '1', '1'); INSERT INTO `user_authority` VALUES ('2', '1', '2'); INSERT INTO `user_authority` VALUES ('3', '2', '2'); INSERT INTO `user_authority` VALUES ('4', '2', '3'); INSERT INTO `user_authority` VALUES ('5', '3', '1'); INSERT INTO `user_authority` VALUES ('6', '2', '2'); |
4、熟悉MVC三層架構的小夥伴們,開始編寫我們domain曾實體代碼
/** * 用戶授權角色類 * @author Administrator * */ @Entity(name="Authority") public class Authority implements GrantedAuthority , Serializable{ @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @Size(min = 2, max = 32) @Column(name="auth_name" , nullable = false, length = 20) // 映射为字段,值不能为空 private String authName; public Authority() { super(); } @Override public String toString() { return "Authority [id=" + id + ", authName=" + authName + "]"; } public Authority(Long id, String authName) { super(); this.id = id; this.authName = authName; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getAuthName() { return authName; } public void setAuthName(String authName) { this.authName = authName; } @Override public String getAuthority() { //權限名稱 return authName; } } |
/** * 用户实体 * * @author Administrator * */ @Entity(name="user") // 實體 public class User implements UserDetails, Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 自動增長 private Long id; // 实体一个唯一标识 @Size(min = 2, max = 32) @Column(name="user_name" , nullable = false, length = 20) // 映射为字段,值不能为空 private String username; @Size(max = 99) @Column(name="pass_word" , length = 99) private String password; @Size(max = 50) @Email @Column(nullable = false, length = 50, unique = true) private String email; @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER) /** * 加入中間表user_authority * 中間表加入列user_id,參考列為當前主鍵列 * 中間表加入列authority_id, 參考當前表倒置到Authority表的主鍵列 */ @JoinTable(name = "user_authority", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id")) private List<Authority> authorities; public User() { } public User(Long id, String username, String password, String email) { super(); this.id = id; this.username = username; this.password = password; this.email = email; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username;
public String getPassword() { return password; } /** * 密碼BCrypt加密 * @param password */ public void setPassword(String password) { PasswordEncoder encoder = new BCryptPasswordEncoder(); String encodePasswd = encoder.encode(password); this.password = encodePasswd; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } /** * 需将 List<Authority> 转成 List<SimpleGrantedAuthority>,否则前端拿不到角色列表名称 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>(); for(GrantedAuthority authority : this.authorities){ simpleAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority())); } return simpleAuthorities; } @Override public boolean isAccountNonExpired() { //重載默認是false,我們要改成true return true; } @Override public boolean isAccountNonLocked() { //重載默認是false,我們要改成true return true; } @Override public boolean isCredentialsNonExpired() { //重載默認是false,我們要改成true return true; } @Override public boolean isEnabled() { //重載默認是false,我們要改成true return true; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", authorities=" + authorities + "]"; } } |
編寫我們repository層的代碼
/** * 實現JPA接口類 * @author Administrator * */ public interface AuthorityRepository extends JpaRepository<Authority, Long>{ } |
/** * UserRepository 接口 * @author Administrator * */ public interface UserRepository extends JpaRepository<User, Long>{ /** * 通過名稱查詢用戶 * @param username * @return */ public User findByUsername(String username); /** * 通過用戶名和密碼查詢用戶 * @param username * @param password * @return */ public User findByUsernameAndPassword(String username , String password); } |
編寫我們service層的代碼
/** * * UserService 服務層<br> * UserService 實現 UserDetailsService 接口,重寫其方法!這是必須<br> * * @author Administrator * */ @Service("userservice") @SuppressWarnings("all") public class UserService implements UserDetailsService{ @Autowired UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if(user == null ) { throw new UsernameNotFoundException("用户名不存在"); } Boolean locked = user.isAccountNonLocked(); return user; } public User findByUsernameAndPassword(String username , String password) { return userRepository.findByUsernameAndPassword(username, password); } } |
/** * * @author Administrator * */ public class AuthorityService { } |
編寫我們controller層的代碼
/** * 主頁控制器 * @author Administrator * */ @Controller public class MainController { @Autowired @Qualifier("userRepository") private UserRepository userRepository; /** * 根目錄控制 * @return */ @GetMapping("/") public String root() { return "redirect:/index"; } /** * 網站首頁 * @return */ @GetMapping("/index") public String index() { return "index"; //index.html } /** * 登陸界面 * @return */ @GetMapping("/login") public String login() { return "login"; //login.html } /** * 403錯誤界面 * @return */ @GetMapping("/403") public String error403() { return "/error/403"; } /** * 登陸錯誤,返回登陸頁面,并添加錯誤信息 * @param model * @return */ @GetMapping("/login-error") public String loginError(Model model , HttpSession session , @RequestParam(value = "secError", required = true) Boolean secError) { model.addAttribute("loginError" , true); //獲取其service層獲取登陸用戶抛出的異常信息 Exception exception = (Exception)session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION"); model.addAttribute("errorMessage" ,exception.getMessage()); return "login"; } } |
/** * 用户控制层 * * @author Administrator * */ @RestController @RequestMapping("/user") public class UserController { @Autowired @Qualifier("userRepository") private UserRepository userRepository; /** * 页面获取用户列表 * * @param model * @return */ @GetMapping("/list") public ModelAndView list(Model model) { model.addAttribute("userList", userRepository.findAll()); model.addAttribute("title", "用户管理"); return new ModelAndView("users/list", "userModel", model); } /** * 根据id查询用户并页面展示 * * @param id * @param model * @return */ @GetMapping("/view/{id}") public ModelAndView view(@PathVariable("id") Long id, Model model) { Optional<User> userOp = userRepository.findById(id); model.addAttribute("user", userOp.get()); model.addAttribute("title", "查看用户"); return new ModelAndView("users/view", "userModel", model); } /** * 获取创建表单页面 * * @param model * @return */ @GetMapping("/form") public ModelAndView createForm(Model model) { model.addAttribute("user", new User()); model.addAttribute("title", "创建用户"); return new ModelAndView("users/form", "userModel", model); } /** * 保存用戶 * @param user * @return */ @PostMapping("/submit") public ModelAndView saveOrUpdateUser(User user) { user = userRepository.save(user); ModelAndView mav = new ModelAndView(); mav.setViewName("redirect:list");// 重定向至list映射方法 return mav; } /** * 根据id查詢用戶帶參并跳轉到form頁面 * * @param id * @param model * @return */ @GetMapping("/modify/{id}") public ModelAndView modifyUser(@PathVariable("id") Long id, Model model) { Optional<User> userOp = userRepository.findById(id); model.addAttribute("user", userOp.get()); model.addAttribute("title", "修改用户"); return new ModelAndView("users/form", "userModel", model); } /** * 根据id刪除用戶 * * @param id * @param model * @return */ @GetMapping("/delete/{id}") public ModelAndView deleteUser(@PathVariable("id") Long id, Model model) { userRepository.deleteById(id); ModelAndView mav = new ModelAndView(); mav.setViewName("redirect:/user/list");// 重定向至list映射方法 return mav; } } |
既然要做權限控制,那麽我們肯定就要編寫springSecurity配置類
/** * springSecurity安全配置类 * 注意:該類上的注解包含"@Configuration"因此只會被初始化加載 * * springSecurity5.0.0以後角色是角色,權限是權限,兩者不能混爲一談 * * @author Administrator * */ @EnableWebSecurity // 配置注解,啓動web認證安全 //@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法级别的权限认证 public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 允许所有用户访问"/bootstrap/**"和"/index" http.authorizeRequests() .antMatchers("/bootstrap/css/**", "/bootstrap/js/**", "/bootstrap/fonts/**", "/").permitAll()// 都可以訪問 .antMatchers("/user/**").hasAnyAuthority("ADMIN") // 需要相應的授權才能訪問 .and().formLogin() // 基於form表單登陸驗證 .loginPage("/login").failureUrl("/login-error?secError=true") // 若用戶錯誤,則跳轉自定義的登陸界面 // .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler()) // 处理异常,handler方式,拒绝访问就重定向到 403 页面 .and().exceptionHandling().accessDeniedPage("/403") // 处理异常,拒绝访问就重定向到 403 页面 ; } /** * */ @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService()); auth.authenticationProvider(authenticationProvider());
// 若要方便與測試那麽就要和passwordEncoder() 返回的 passwordEncoder() 實現類前呼後應 。認證信息儲存與内存中 // auth.inMemoryAuthentication() // .withUser("baihoo").password("12345").roles("ADMIN"); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // 使用 BCrypt 加密,注意:原生密碼也得采用這個加密類加密 // return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Bean public UserService userService() { return new UserService(); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); // UserService 繼承UserDetailsService,實現其 loadUserByUsername() 方法。 authenticationProvider.setUserDetailsService(userService()); // 设置密码加密方式 authenticationProvider.setPasswordEncoder(passwordEncoder()); return authenticationProvider; } @Bean public AccessDeniedHandler accessDeniedHandler() { return new MyAccessDeniedHandler(); } } |
注意:springSecurity5.0.0以後角色是角色,權限是權限,兩者不能混爲一談,這可是小編我遇到第一個坑 |
自定義拒絕訪問handler,小編我本來是可以不寫,對整個案例工程沒什麽影響, 好學小夥伴可以看看,寫的不好多多見諒!
/** * 定义403无权限访问的处理,重定向到/403页面 * * @author Administrator * */ public class MyAccessDeniedHandler implements AccessDeniedHandler { private static Logger logger = LoggerFactory.getLogger(MyAccessDeniedHandler.class); @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { logger.info("用戶: '" + auth.getName() + "'試圖訪問受保護的 URL: " + request.getRequestURI()); } response.sendRedirect(request.getContextPath() + "/403"); } } |
5、著手開始編寫我們前端html代碼!
該目錄下/resources/templates/fragments/ 著手編寫header.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" th:fragment="header"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>bootsrap in action</title> <link href="../../static/bootstrap/css/tether.min.css" th:href="@{/bootstrap/css/tether.min.css}" rel="stylesheet"> <!-- bootstrap css样式 --> <link href="../../static/bootstrap/css/bootstrap.min.css" th:href="@{/bootstrap/css/bootstrap.min.css}" rel="stylesheet"> <!-- 样式文字css样式 --> <link href="../../static/bootstrap/css/font-awesome.min.css" th:href="@{/bootstrap/css/font-awesome.min.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/nprogress.css" th:href="@{/bootstrap/css/nprogress.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/thinker-md.vendor.css" th:href="@{/bootstrap/css/thinker-md.vendor.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/bootstrap-tagsinput.css" th:href="@{/bootstrap/css/bootstrap-tagsinput.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/component-chosen.min.css" th:href="@{/bootstrap/css/component-chosen.min.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/toastr.min.css" th:href="@{/bootstrap/css/toastr.min.css}" rel="stylesheet"> <!-- 图片编辑的css --> <link href="../../static/bootstrap/css/cropbox.css" th:href="@{/bootstrap/css/cropbox.css}" rel="stylesheet"> <!-- 自定义的样式 --> <link href="../../static/bootstrap/css/style.css" th:href="@{/bootstrap/css/style.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/thymeleaf-bootstrap-paginator.css" th:href="@{/bootstrap/css/thymeleaf-bootstrap-paginator.css}" rel="stylesheet"> <link href="../../static/bootstrap/css/blog.css" th:href="@{/bootstrap/css/blog.css}" rel="stylesheet"> </head> <body> <nav class="navbar navbar-inverse bg-inverse navbar-toggleable-md"> <div class="container"> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarsContainer"> <span class="navbar-toggler-icon"></span> </button> <a class="navbar-brand" href="/" th:href="@{~/}">baiHoo Blog</a> <div class="collapse navbar-collapse" id="navbarsContainer"> <ul class="navbar-nav mr-auto"> <li class="nav-item"> <!-- @{~/user} 相对于服务器下面 --> <a class="nav-link" th:href="@{~/}">首页<span class="sr-only">(current)</span></a> </li> </ul> <!-- 曲義解釋: 授權認證--已認證,并证明是真实的、可靠的。用戶已登陸 --> <!-- 登陸判斷 --> <div sec:authorize="isAuthenticated()" class="row"> <ul class="navbar-nav mr-auto"> <li class="nav-item"> <span class="nav-link" sec:authentication="name"></span> </li> </ul> <form action="/logout" th:action="@{~/logout}" method="post"> <!-- logout在我編寫的controller是沒有的,該logout遵循springSecurity本身規則的,注銷用戶 --> <input class="btn btn-outline-success" type="submit" value="退出"> </form> </div> <!-- 曲義解釋: 授權認證--未認證,匿名未登陸的 --> <div sec:authorize="isAnonymous()"> <a href="/login" th:href="@{~/login}" class="btn btn-outline-success my-2 my-sm-0" type="submit">登陸</a> </div> </div> </div> </nav> </body> </html> |
該目錄下/resources/templates/fragments/ 著手編寫footer.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <head> <meta charset="UTF-8"> </head> <body> <footer class="blog-footer bg-inverse" data-th-fragment="footer" th:fragment="footer"> <!-- <a id="goToTop" href="#"> <i class="fa fa-chevron-up fa-3x" aria-hidden="true"></i> </a> --> <div class="container"> <p class="m-0 text-center text-white">© 2018 <a href="https://baihoo.com">baihoo.com</a></p> </div> <!-- JavaScript --> <script src="../../static/bootstrap/js/jquery-3.1.1.min.js" th:src="@{/bootstrap/js/jquery-3.1.1.min.js}"></script> <script src="../../static/bootstrap/js/jquery.form.min.js" th:src="@{/bootstrap/js/jquery.form.min.js}"></script> <script src="../../static/bootstrap/js/tether.min.js" th:src="@{/bootstrap/js/tether.min.js}"></script> <script src="../../static/bootstrap/js/bootstrap.min.js" th:src="@{/bootstrap/js/bootstrap.min.js}"></script> <script src="../../static/bootstrap/js/nprogress.js" th:src="@{/bootstrap/js/nprogress.js}"></script> <script src="../../static/bootstrap/js/thinker-md.vendor.min.js" th:src="@{/bootstrap/js/thinker-md.vendor.min.js}"></script> <script src="../../static/bootstrap/js/jquery.tag-editor.min.js" th:src="@{/bootstrap/js/jquery.tag-editor.min.js}"></script> <script src="../../static/bootstrap/js/chosen.jquery.js" th:src="@{/bootstrap/js/chosen.jquery.js}"></script> <script src="../../static/bootstrap/js/toastr.min.js" th:src="@{/bootstrap/js/toastr.min.js}"></script> <script src="../../static/bootstrap/js/cropbox.js" th:src="@{/bootstrap/js/cropbox.js}"></script> <script src="../../static/bootstrap/js/thymeleaf-bootstrap-paginator.js" th:src="@{/bootstrap/js/thymeleaf-bootstrap-paginator.js}"></script> <script src="../../static/bootstrap/js/catalog-generator.js" th:src="@{/bootstrap/js/catalog-generator.js}"></script> <script src="../../static/bootstrap/js/main.js" th:src="@{/bootstrap/js/main.js}"></script> </footer> </body> </html> |
該目錄下/resources/templates/error/ 著手編寫403.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <!-- thymeleaf模板集合springsecurity標簽權限控制,的命名空間 --> <!-- 引用替换当前的头部信息html 曲义解释: 引用header.html 定义的 header片段 --> <head th:replace="~{fragments/header :: header}"> <meta charset="UTF-8"> </head> <body> <!-- page content --> <div class="container blog-content-container"> <div class="starter-template"> <h1>403 - 没有访问权限</h1><div th:inline="text">Sorry! '[[${#httpServletRequest.remoteUser}]]', 你没有权限访问此页面.</div></div> </div> <div th:replace="~{fragments/footer :: footer}"></div> </body> </html> |
該目錄下/resources/users/error/ 編寫三個html文件
form.tml
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <!-- 引用替换当前的头部信息html 曲义解释: 引用header.html 定义的 header片段 --> <head th:replace="~{fragments/header :: header}"> <meta charset="UTF-8"> </head> <body> <!-- page content --> <div class="container blog-content-container"> <h3 th:text="${userModel.title}">原型效果 baihoo</h3> <!-- 获取userModel绑定user --> <form action="/user/submit" th:action="@{/user/submit}" method="post" th:object="${userModel.user}"> <input type="hidden" name="id" th:value="*{id}"> <!-- 直接取出当前对象属性的值 -->名称:<br> <input type="text" class="form-control" name="username" th:value="*{username}"><br> <!-- 直接取出当前对象属性的值 -->密碼:<br> <input type="password" class="form-control" name="password" th:value="*{password}"><br> <!-- 直接取出当前对象属性的值 -->邮箱:<br> <input type="text" class="form-control" name="email" th:value="*{email}"><br> <!-- 直接取出当前对象属性的值 --><input type="submit" class="btn btn-default" value="提交"> </form> </div> <div th:replace="~{fragments/footer :: footer}"></div> </body> </html> |
list.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <!-- 引用替换当前的头部信息html 曲义解释: 引用header.html 定义的 header片段 --> <head th:replace="~{fragments/header :: header}"> <meta charset="UTF-8"> </head> <body> <!-- page content --> <div class="container blog-content-container"> <h3 th:text="${userModel.title}">原型效果 baihoo</h3> <div> <a class="btn btn-default" href="/user/form.html" th:href="@{/user/form}">创建用户</a> </div> <table class="table table-striped"> <thead><tr> <td>ID</td><td>Email</td><td>用户名</td> </tr></thead> <tbody> <!-- 判断userModel.userList 是否没有值 --><tr th:if="${userModel.userList.size() eq 0}"> <td colspan="3">没有用户信息!</td></tr> <!-- th:each 迭代 --> <tr th:each="user : ${userModel.userList}"> <td th:text="${user.id}"></td> <td th:text="${user.email}"></td> <td> <!-- 带参数的连接 --> <a href="view/view.html" th:href="@{'/user/view/'+${user.id}}" th:text="${user.username}"></a> </td> </tr> </tbody> <tfoot> <tr> <td></td> <td></td> <td></td> </tr> </tfoot> </table> </div> <div th:replace="~{fragments/footer :: footer}"></div> </body> </html> |
view.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <!-- 引用替换当前的头部信息html 曲义解释: 引用header.html 定义的 header片段 --> <head th:replace="~{fragments/header :: header}"> <meta charset="UTF-8"> </head> <body> <!-- page content --> <div class="container blog-content-container"> <h3 th:text="${userModel.title}">原型效果 baihoo</h3> <div> <p> <strong>Id:</strong> <span th:text="${userModel.user.id}"></span> </p> <p> <strong>name:</strong> <span th:text="${userModel.user.username}"></span> </p> <p> <strong>email:</strong> <span th:text="${userModel.user.email}"></span> </p> </div> <div> <a th:href="@{'/user/modify/'+${userModel.user.id}}">修改用戶</a> <a th:href="@{'/user/delete/'+${userModel.user.id}}">刪除用戶</a> </div> </div> <div> <div th:replace="~{fragments/footer :: footer}"></div> </div> </body> </html> |
該目錄下/resources/編寫
index.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <!-- thymeleaf模板集合springsecurity標簽權限控制,的命名空間 -->
<!-- 引用替换当前的头部信息html 曲义解释: 引用header.html 定义的 header片段 --> <head th:replace="~{fragments/header :: header}"> <meta charset="UTF-8"> </head> <body> <!-- page content --> <div class="container blog-content-container"> <!-- 曲義解釋: 授權認證--已認證,并证明是真实的、可靠的。用戶已登陸 --> <div sec:authorize="isAuthenticated()"> <p>已有用戶登陸</p> <p>登陸用戶:<span sec:authentication="name"></span></p> <!-- principal:主要演员,主角; 表示顯示用戶角色 --> <p>用戶角色:<span sec:authentication="principal.authorities"></span></p> </div> <!-- 曲義解釋: 授權認證--未認證,匿名未登陸的 --> <div sec:authorize="isAnonymous()"> <p>用戶未登陸</p> </div> </div> <div th:replace="~{fragments/footer :: footer}"></div> </body> </html> |
login.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
> <!-- thymeleaf模板集合springsecurity標簽權限控制,的命名空間 -->
<!-- 引用替换当前的头部信息html 曲义解释: 引用header.html 定义的 header片段 --> <head th:replace="~{fragments/header :: header}"> <meta charset="UTF-8"> </head> <body> <!-- page content --> <div class="container blog-content-container"> <!-- login在我們編寫的controller中是只是做跳轉頁的url , 并不對登陸用戶做實際操作,該login遵循springSecurity本身規則的,登陸用戶 --> <form action="/login" th:action="@{/login}" method="post"> <h2>請登陸</h2> <div class="form-group col-md-5"> <label for="username" class="col-form-label">賬號</label> <input type="text" class="form-control" placeholder="請輸入用戶名" maxlength="25" name="username" id="username"> </div> <div class="form-group col-md-5"> <label for="password" class="col-form-label">密碼</label> <input type="password" class="form-control" placeholder="請輸入密碼" maxlength="32" name="password" id="password"> </div> <div class="form-group col-md-5"> <input type="checkbox" name="remember-me"/>记住我 <br> </div> <div class="form-group col-md-5"> <input class="btn btn-primary" type="submit" value="登陸"> </div> <div class="col-md-5" th:if="${loginError}"> <p class="blog-label-error" th:text="${errorMessage}"></p> </div> </form> </div> <div th:replace="~{fragments/footer :: footer}"></div> </body> </html> |
6、 目前爲止,我們的工程代碼算是,交代完畢了,看下頁面效果!http://localhost:8080/index
沒有admin權限的用戶登陸
若訪問http://localhost:8080/user/list,會抛出沒有權限操作!
有admin權限的用戶登陸
若訪問http://localhost:8080/user/list, 直接允許訪問
贅言:
我們整個的 springBoot 2.0.3 + SpringSecurity 5.0.6 + thymeleaf + boostrap 權限管理案例,算告一段落了,需要源碼的小夥伴,直接下載我的github源碼