首先,添加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