首先,添加maven依賴,完整的pom文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.1.6.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.hui</groupId> 12 <artifactId>SpringBoot22</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>SpringBoot22</name> 15 <description>Demo project for Spring Boot</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter-web</artifactId> 25 </dependency> 26 27 <dependency> 28 <groupId>mysql</groupId> 29 <artifactId>mysql-connector-java</artifactId> 30 <scope>runtime</scope> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-starter-test</artifactId> 35 <scope>test</scope> 36 </dependency> 37 <dependency> 38 <groupId>org.apache.shiro</groupId> 39 <artifactId>shiro-spring</artifactId> 40 <version>1.4.1</version> 41 </dependency> 42 <dependency> 43 <groupId>org.springframework.boot</groupId> 44 <artifactId>spring-boot-starter-data-jpa</artifactId> 45 <version>RELEASE</version> 46 </dependency> 47 <dependency> 48 <groupId>org.springframework.boot</groupId> 49 <artifactId>spring-boot-starter-thymeleaf</artifactId> 50 <version>RELEASE</version> 51 </dependency> 52 </dependencies> 53 54 <build> 55 <plugins> 56 <plugin> 57 <groupId>org.springframework.boot</groupId> 58 <artifactId>spring-boot-maven-plugin</artifactId> 59 </plugin> 60 </plugins> 61 </build> 62 63 </project>
接着,我們先編寫自定義的Realm類(MyJbdcRealm)
1 package com.hui.SpringBoot22.realm; 2 3 import org.apache.shiro.authc.*; 4 import org.apache.shiro.authz.AuthorizationException; 5 import org.apache.shiro.authz.AuthorizationInfo; 6 import org.apache.shiro.authz.SimpleAuthorizationInfo; 7 import org.apache.shiro.realm.AuthorizingRealm; 8 import org.apache.shiro.subject.PrincipalCollection; 9 import org.apache.shiro.util.ByteSource; 10 import org.apache.shiro.util.JdbcUtils; 11 12 import javax.sql.DataSource; 13 import java.sql.Connection; 14 import java.sql.PreparedStatement; 15 import java.sql.ResultSet; 16 import java.sql.SQLException; 17 import java.util.Arrays; 18 import java.util.Collection; 19 import java.util.LinkedHashSet; 20 import java.util.Set; 21 22 public class MyJdbcRealm extends AuthorizingRealm { 23 24 protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?"; 25 protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?"; 26 protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?"; 27 protected DataSource dataSource; 28 protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY; 29 protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY; 30 protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY; 31 protected boolean permissionsLookupEnabled = false; 32 33 public void setDataSource(DataSource dataSource) { 34 this.dataSource = dataSource; 35 } 36 37 public void setAuthenticationQuery(String authenticationQuery) { 38 this.authenticationQuery = authenticationQuery; 39 } 40 41 public void setUserRolesQuery(String userRolesQuery) { 42 this.userRolesQuery = userRolesQuery; 43 } 44 45 public void setPermissionsQuery(String permissionsQuery) { 46 this.permissionsQuery = permissionsQuery; 47 } 48 49 public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) { 50 this.permissionsLookupEnabled = permissionsLookupEnabled; 51 } 52 53 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 54 UsernamePasswordToken upToken = (UsernamePasswordToken) token; 55 String username = upToken.getUsername(); 56 if (username == null) { 57 throw new AccountException("Null usernames are not allowed by this realm."); 58 } 59 Connection conn = null; 60 SimpleAuthenticationInfo info = null; 61 try { 62 conn = dataSource.getConnection(); 63 String password = null; 64 password = getPasswordForUser(conn, username); 65 if (password == null) { 66 throw new UnknownAccountException("No account found for user [" + username + "]"); 67 } 68 info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName()); 69 } catch (SQLException e) { 70 final String message = "There was a SQL error while authenticating user [" + username + "]"; 71 throw new AuthenticationException(message, e); 72 } finally { 73 JdbcUtils.closeConnection(conn); 74 } 75 return info; 76 } 77 78 private String getPasswordForUser(Connection conn, String username) throws SQLException { 79 String result = null; 80 PreparedStatement ps = null; 81 ResultSet rs = null; 82 try { 83 ps = conn.prepareStatement(authenticationQuery); 84 ps.setString(1, username); 85 rs = ps.executeQuery(); 86 boolean foundResult = false; 87 while (rs.next()) { 88 if (foundResult) { 89 throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique."); 90 } 91 result = rs.getString(1); 92 foundResult = true; 93 } 94 } finally { 95 JdbcUtils.closeResultSet(rs); 96 JdbcUtils.closeStatement(ps); 97 } 98 return result; 99 } 100 101 @Override 102 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 103 if (principals == null) { 104 throw new AuthorizationException("PrincipalCollection method argument cannot be null."); 105 } 106 String username = (String) getAvailablePrincipal(principals); 107 Connection conn = null; 108 Set<String> roleNames = null; 109 Set<String> permissions = null; 110 try { 111 conn = dataSource.getConnection(); 112 roleNames = getRoleNamesForUser(conn, username); 113 if (permissionsLookupEnabled) { 114 permissions = getPermissions(conn, username, roleNames); 115 } 116 } catch (SQLException e) { 117 final String message = "There was a SQL error while authorizing user [" + username + "]"; 118 throw new AuthorizationException(message, e); 119 } finally { 120 JdbcUtils.closeConnection(conn); 121 } 122 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames); 123 info.setStringPermissions(permissions); 124 return info; 125 } 126 127 protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException { 128 PreparedStatement ps = null; 129 ResultSet rs = null; 130 Set<String> roleNames = new LinkedHashSet<String>(); 131 try { 132 ps = conn.prepareStatement(userRolesQuery); 133 ps.setString(1, username); 134 rs = ps.executeQuery(); 135 while (rs.next()) { 136 String roleName = rs.getString(1); 137 if (roleName != null) { 138 roleNames.add(roleName); 139 } 140 } 141 } finally { 142 JdbcUtils.closeResultSet(rs); 143 JdbcUtils.closeStatement(ps); 144 } 145 return roleNames; 146 } 147 148 protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException { 149 PreparedStatement ps = null; 150 Set<String> permissions = new LinkedHashSet<String>(); 151 try { 152 ps = conn.prepareStatement(permissionsQuery); 153 for (String roleName : roleNames) { 154 ps.setString(1, roleName); 155 ResultSet rs = null; 156 try { 157 rs = ps.executeQuery(); 158 while (rs.next()) { 159 String permissionString = rs.getString(1); 160 String[] permissionNames = permissionString.split(","); 161 permissions.addAll(Arrays.asList(permissionNames)); 162 } 163 } finally { 164 JdbcUtils.closeResultSet(rs); 165 } 166 } 167 } finally { 168 JdbcUtils.closeStatement(ps); 169 } 170 return permissions; 171 } 172 }
MyJdbcRealm類就是從shiro中原有的JdbcRealm類copy來的,去掉了與密碼鹽(salt)有關的部分。
我在數據庫的權限表中添加的權限字段值為 “user:add,user:delete,user:update,user:select,user:updateRole” 的格式,而shiro中原有的JdbcRealm中會將這一長串當成一種權限來看,在 MyJdbcRealm 類中改寫了 JdbcRealm中的 getPermissions(Connection conn, String username, Collection<String> roleNames) 方法(具體看159-161行)來使權限字段的值為 5種 不同的權限。
當然,你也可以在自定義的 Realm 類中重寫 doGetAuthorizationInfo(PrincipalCollection principals) 方法,來實現自己的權限管理。
接着,編寫shiro的配置類(ShiroConfiguration)
1 package com.hui.SpringBoot22; 2 3 import com.hui.SpringBoot22.realm.MyJdbcRealm; 4 import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 5 import org.apache.shiro.mgt.SecurityManager; 6 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 7 import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 8 import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.beans.factory.annotation.Qualifier; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Configuration; 13 import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; 14 15 import javax.sql.DataSource; 16 import java.util.*; 17 18 @Configuration 19 public class ShiroConfiguration { 20 21 @Autowired 22 private DataSource dataSource; 23 24 @Bean(name = "shiroFilter") 25 public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){ 26 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 27 shiroFilterFactoryBean.setSecurityManager(securityManager); 28 //配置login頁、登陸成功頁、沒有權限頁 29 shiroFilterFactoryBean.setLoginUrl("/"); 30 shiroFilterFactoryBean.setSuccessUrl("/index"); 31 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); 32 33 //配置訪問權限(順序執行攔截) 34 // “/**” 放到最下面,如果將("/**","authc")放到("/userLogin","anon")的上面 35 // 則“/userLogin”可能會被攔截 36 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); 37 filterChainDefinitionMap.put("/logout","logout"); 38 filterChainDefinitionMap.put("/userLogin","anon"); 39 filterChainDefinitionMap.put("/403","roles"); 40 filterChainDefinitionMap.put("/**","authc"); 41 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 42 return shiroFilterFactoryBean; 43 } 44 45 @Bean(name = "securityManager") 46 public SecurityManager securityManager(@Qualifier("myJdbcRealm")MyJdbcRealm myJdbcRealm){ 47 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 48 securityManager.setRealm(myJdbcRealm); 49 return securityManager; 50 } 51 52 @Bean(name = "myJdbcRealm") 53 public MyJdbcRealm myJdbcRealm(@Qualifier("credentialsMatcher") HashedCredentialsMatcher credentialsMatcher, 54 @Qualifier("dataSource") DataSource dataSource){ 55 MyJdbcRealm myJdbcRealm = new MyJdbcRealm(); 56 //打開shiro的權限 (默認為false) (不開啟則不會檢查權限 --> 點擊“修改”,不管有沒有權限都能進行跳轉) 57 myJdbcRealm.setPermissionsLookupEnabled(true); 58 //設置datasource 59 myJdbcRealm.setDataSource(dataSource); 60 //設置密碼加密器 61 myJdbcRealm.setCredentialsMatcher(credentialsMatcher); 62 //設置登陸驗證sql語句 63 String sql = "select password from test_user where username = ?"; 64 myJdbcRealm.setAuthenticationQuery(sql); 65 //設置權限驗證sql語句 66 String permissionSql = "select permission from permissions where role_name = ?"; 67 myJdbcRealm.setPermissionsQuery(permissionSql); 68 return myJdbcRealm; 69 } 70 71 //設置加密算法為MD5。加密次數為1 72 @Bean(name = "credentialsMatcher") 73 public HashedCredentialsMatcher credentialsMatcher(){ 74 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); 75 credentialsMatcher.setHashAlgorithmName("md5"); 76 credentialsMatcher.setHashIterations(1); 77 return credentialsMatcher; 78 } 79 80 /** 81 * 開啟aop注解支持 -- 借助SpringAOP掃描使用shiro注解的類 82 * (不開啟則不能掃描到shiro的@RequiresPermissions等注解) 83 * @param securityManager 84 * @return 85 */ 86 @Bean 87 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { 88 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); 89 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); 90 return authorizationAttributeSourceAdvisor; 91 } 92 93 //配置無權限異常處理,跳轉到403 94 @Bean(name="simpleMappingExceptionResolver") 95 public SimpleMappingExceptionResolver 96 createSimpleMappingExceptionResolver() { 97 SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver(); 98 Properties mappings = new Properties(); 99 mappings.setProperty("DatabaseException", "databaseError");//數據庫異常處理 100 mappings.setProperty("UnauthorizedException", "403"); 101 r.setExceptionMappings(mappings); // None by default 102 r.setDefaultErrorView("error"); // No default 103 r.setExceptionAttribute("ex"); // Default is "exception" 104 return r; 105 } 106 107 }
上面代碼中有幾個需要注意的點:
第一點:是再定義的名為“securityManager”的 Bean 中,使用的是 DefaultWebSecurityManager 這個類,而不是 DefaultSecurityManager(使用DefaultSecurityManager類會報錯),前者是 org.apache.shiro.web.mgt 包下的,與web有關;后者是 org.apache.shiro.mgt 包下的。
第二點:開啟aop注解支持 -- 借助SpringAOP掃描使用shiro注解的類,開啟之后可以掃描到 Controller 類上的shiro注解(例如:@RequiresPermissions、@RequiresRoles等)
第三點:配置無權限異常處理,這樣就會攔截到沒有權限的用戶,然后跳轉到403頁面(
這里配置無權限異常處理是為了配合shiro注解。
如果不想使用shiro注解,也可以不配置該異常處理,直接在攔截鏈“filterChainDefinitionMap”中配置 -- 例如:/userList= roles["admin","admin1"] --> 表明訪問路徑 /userList 需要同時具備“admin”和“admin1”的角色,不合條件則403; 另一種與角色攔截相似:權限攔截 -- /userList= perms["user:select"]
)
第四點:在名為 “myJdbcRealm” 的Bean中,設置登陸驗證與權限驗證的sql查詢語句,方法分別是 setAuthenticationQuery("select password from test_user where username = ?") 、 setPermissionsQuery("select permission from permissions where role_name = ?")
之所以執行這兩個setXxx()方法,是因為我這里的實體類對應生成的表名、字段名與shiro默認的不一致(如果你想使用shiro默認的,那么你就需要按照shiro源碼中的sql語句來設置實體生成的表名、字段名與shiro默認的一致即可)
接下來,編寫實體類
User類
1 package com.hui.SpringBoot22.pojo; 2 3 import javax.persistence.*; 4 5 @Entity 6 @Table(name = "test_user") 7 public class User { 8 @Id 9 @GeneratedValue(strategy = GenerationType.IDENTITY)//默認為AUTO,這里設置為自增 10 private Long id; 11 @Column(name = "username",length = 50) 12 private String username; 13 @Column(name = "password",length = 50) 14 private String password; 15 16 public Long getId() { 17 return id; 18 } 19 20 public void setId(Long id) { 21 this.id = id; 22 } 23 24 public String getUsername() { 25 return username; 26 } 27 28 public void setUsername(String username) { 29 this.username = username; 30 } 31 32 public String getPassword() { 33 return password; 34 } 35 36 public void setPassword(String password) { 37 this.password = password; 38 } 39 }
Role類
1 package com.hui.SpringBoot22.pojo; 2 3 import javax.persistence.*; 4 5 @Entity 6 @Table(name = "user_roles") 7 public class Role { 8 @Id 9 @GeneratedValue(strategy = GenerationType.IDENTITY) 10 private Long id; 11 @Column(name = "username",length = 50) 12 private String username; 13 @Column(name = "role_name",length = 50) 14 private String roles; 15 16 public Long getId() { 17 return id; 18 } 19 20 public void setId(Long id) { 21 this.id = id; 22 } 23 24 public String getUsername() { 25 return username; 26 } 27 28 public void setUsername(String username) { 29 this.username = username; 30 } 31 32 public String getRoles() { 33 return roles; 34 } 35 36 public void setRoles(String roles) { 37 this.roles = roles; 38 } 39 }
Permission類
1 package com.hui.SpringBoot22.pojo; 2 3 import javax.persistence.*; 4 5 @Entity 6 @Table(name = "permissions") 7 public class Permission { 8 @Id 9 @GeneratedValue(strategy = GenerationType.IDENTITY) 10 private Long id; 11 @Column(name = "role_name",length = 50) 12 private String roleName; 13 @Column(name = "permission",length = 120) 14 private String permissions; 15 16 public Long getId() { 17 return id; 18 } 19 20 public void setId(Long id) { 21 this.id = id; 22 } 23 24 public String getRoleName() { 25 return roleName; 26 } 27 28 public void setRoleName(String roleName) { 29 this.roleName = roleName; 30 } 31 32 public String getPermissions() { 33 return permissions; 34 } 35 36 public void setPermissions(String permissions) { 37 this.permissions = permissions; 38 } 39 }
這里就不多說了,主要注意的就是對應的表名、字段名要與 ShiroConfiguration 類中的sql語句的表名、字段名一致。
接下來是Repository編寫,直接看代碼好了
1 package com.hui.SpringBoot22.repository; 2 3 import com.hui.SpringBoot22.pojo.User; 4 import org.springframework.data.jpa.repository.JpaRepository; 5 import org.springframework.data.jpa.repository.Modifying; 6 import org.springframework.data.jpa.repository.Query; 7 8 import javax.transaction.Transactional; 9 10 public interface UserRepository extends JpaRepository<User,Long> { 11 User findUserById(Long id); 12 13 @Transactional 14 @Modifying 15 @Query("update User set username=?2,password=?3 where id=?1") 16 int updateUserById(Long id,String username,String password); 17 18 @Transactional 19 @Modifying 20 @Query("delete from User where id=?1") 21 void deleteUserById(Long id); 22 }
這里繼承了JpaRepository類,就不用再類上加Spring注解來將其注入(因為 JpaRepository 類上有一個@NoRepositoryBean注解,原理咱不懂!!!)
select、delete之類的語句 Jpa 已經封裝了一部分方法,我們可以直接調用,如 save(S entity)、delete(T entity)等
如果Jpa中封裝的不能滿足需求,那就自己寫啦
像上面的在 UserRepository 類中添加一個方法,然后再方法上加上@Query注解,里面有個value屬性,用來指定編寫的sql語句 --> 如:@Query(value="update ...")
值得注意的是,在 @Query 注解中的 sql 語句對應的表名應寫 實體類名(上面代碼中本人寫的就是實體類 User );關於sql中的字段是不是需要用實體類屬性名,有興趣的朋友可以自己試一下。
如果覺得別扭,可以在@Query注解中編寫 nativeQuery 屬性,使其值為 true ,這樣 Jpa 就能識別原生 sql 了 -->
例子: @Query(nativeQuery = true,
value="select r.id,r.username,r.role_name from user_roles u left join user_roles r on u.username=r.username where u.id=?1")
Role findRoleByUserid(Long id);
最后,如果是insert、delete、update之類的語句,還要在方法上面加上@Modifying和@Transactional注解(等大佬幫我解惑ing...)
接着,再貼一下 controller 的代碼
1 package com.hui.SpringBoot22.controller; 2 3 import com.hui.SpringBoot22.pojo.User; 4 import com.hui.SpringBoot22.repository.UserRepository; 5 import com.hui.SpringBoot22.utils.Md5; 6 import org.apache.shiro.SecurityUtils; 7 import org.apache.shiro.authc.UsernamePasswordToken; 8 import org.apache.shiro.authz.annotation.RequiresPermissions; 9 import org.apache.shiro.subject.Subject; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Controller; 12 import org.springframework.web.bind.annotation.RequestMapping; 13 14 import java.util.List; 15 import java.util.Map; 16 17 @Controller 18 public class UserController { 19 @Autowired 20 private UserRepository userRepository; 21 22 @RequestMapping("/") 23 public String toLogin(){ 24 return "login"; 25 } 26 27 @RequestMapping("/userLogin") 28 public String userLogin(String username, String password, Map<String,Object> map){ 29 Subject subject = SecurityUtils.getSubject(); 30 UsernamePasswordToken token = new UsernamePasswordToken(username,password); 31 try{ 32 subject.login(token); 33 map.put("loginName",username); 34 }catch(Exception e){ 35 map.put("msg","登陸失敗"); 36 return "login"; 37 } 38 return "forward:userList"; 39 } 40 41 @RequestMapping("/userList") 42 @RequiresPermissions("user:select") 43 public String list(Map<String,Object> map){ 44 List<User> users = userRepository.findAll(); 45 map.put("userList",users); 46 return "userList"; 47 } 48 49 @RequestMapping("/toUserAdd") 50 public String toAdd(){ 51 return "userAdd"; 52 } 53 54 @RequestMapping("/userAdd") 55 @RequiresPermissions("user:add") 56 public String userAdd(User user){ 57 user.setPassword(Md5.md5(user.getPassword())); 58 userRepository.save(user); 59 return "forward:userList"; 60 } 61 62 @RequestMapping("/toUserEdit") 63 public String toEdit(Long id,Map<String,Object> map){ 64 User user = userRepository.findUserById(id); 65 map.put("user",user); 66 return "userEdit"; 67 } 68 69 @RequestMapping("/userEdit") 70 @RequiresPermissions("user:update") 71 public String userEdit(Long id,String username,String password){ 72 password = Md5.md5(password); 73 userRepository.updateUserById(id,username,password); 74 return "forward:userList"; 75 } 76 77 @RequestMapping("/userDelete") 78 @RequiresPermissions("user:delete") 79 public String deleteUser(Long id){ 80 userRepository.deleteUserById(id); 81 return "forward:userList"; 82 } 83 }
@RequiresPermissions注解是驗證用戶權限
@RequiresRoles注解是驗證用戶角色(這個在RoleController中用到,這里沒有貼出來)
然后,再看一部分使用 thymeleaf 的HTML代碼
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <div style="margin-left: 30%"> 9 <form action="/roleUpdate" method="post"> 10 <input type="hidden" name="id" th:value="${role.id}"/> 11 用 戶 名:<input name="username" type="text" th:value="${role.username}"/><br/><br/> 12 選擇角色:<input style="margin-left: 8px;" type="radio" name="roles" 13 th:each="roleName,roleNameStat:${roleNameList}" 14 th:value="${roleName}" 15 th:text="${roleName}" 16 th:attr="checked=${roleName==role.roles?true:false}" 17 /><br/><br/> 18 <input type="submit" value="提交"/> 19 </form> 20 </div> 21 </body> 22 </html>
老實說,第一次使用 thymeleaf 真的不大習慣,總是寫成常規的 HTML 代碼
上面也沒什么好說的,也就一個下拉框的遍歷(
th:text 表示文本值, th:value 表示value值,th:attr 表示是否選中狀態,
th:each 就是遍歷后台傳來的 list 集合 --> roleName 代表每一個 list 集合元素,roleNameStat.index 代表着該元素的下標
)
最后,再看一下配置文件 application.properties
1 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 2 spring.datasource.url=jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC 3 spring.datasource.username=xxx 4 spring.datasource.password=xxx 5 6 spring.jpa.hibernate.ddl-auto=create-drop 7 spring.jpa.show-sql=true 8 spring.jpa.database=mysql 9 10 spring.thymeleaf.cache=false 11 spring.thymeleaf.mode=HTML
這個地方有一個坑,就是如果我們設置 spring.jpa.hibernate.ddl-auto=update,就不會執行 resources 目錄下的 import.sql 文件等
根據官方文檔來說,如果需要執行 resources 目錄下的 import.sql 文件,就必須設置 spring.jpa.hibernate.ddl-auto 的值為 create 或者 create-drop
還一種辦法就是不使用 spring.jpa.hibernate.ddl-auto ,直接在 resources 目錄下添加 schema.sql 和 data.sql 文件(schema.sql用來執行DDL語句,data.sql用來執行DML語句)
還有少部分代碼和 HTML 就不貼出來了,有興趣的可以去下載源代碼看看。
項目默認登陸用戶 ==> 用戶名:lmh,密碼:123
項目GitHub地址:https://github.com/Lmh115/SpringBoot