首先,添加maven依赖,完整的pom文件如下:
1 1 <?xml version="1.0" encoding="UTF-8"?> 2 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 4 <modelVersion>4.0.0</modelVersion> 5 5 <parent> 6 6 <groupId>org.springframework.boot</groupId> 7 7 <artifactId>spring-boot-starter-parent</artifactId> 8 8 <version>2.1.6.RELEASE</version> 9 9 <relativePath/> <!-- lookup parent from repository --> 1010 </parent> 1111 <groupId>com.hui</groupId> 1212 <artifactId>SpringBoot22</artifactId> 1313 <version>0.0.1-SNAPSHOT</version> 1414 <name>SpringBoot22</name> 1515 <description>Demo project for Spring Boot</description> 1616 1717 <properties> 1818 <java.version>1.8</java.version> 1919 </properties> 2020 2121 <dependencies> 2222 <dependency> 2323 <groupId>org.springframework.boot</groupId> 2424 <artifactId>spring-boot-starter-web</artifactId> 2525 </dependency> 2626 2727 <dependency> 2828 <groupId>mysql</groupId> 2929 <artifactId>mysql-connector-java</artifactId> 3030 <scope>runtime</scope> 3131 </dependency> 3232 <dependency> 3333 <groupId>org.springframework.boot</groupId> 3434 <artifactId>spring-boot-starter-test</artifactId> 3535 <scope>test</scope> 3636 </dependency> 3737 <dependency> 3838 <groupId>org.apache.shiro</groupId> 3939 <artifactId>shiro-spring</artifactId> 4040 <version>1.4.1</version> 4141 </dependency> 4242 <dependency> 4343 <groupId>org.springframework.boot</groupId> 4444 <artifactId>spring-boot-starter-data-jpa</artifactId> 4545 <version>RELEASE</version> 4646 </dependency> 4747 <dependency> 4848 <groupId>org.springframework.boot</groupId> 4949 <artifactId>spring-boot-starter-thymeleaf</artifactId> 5050 <version>RELEASE</version> 5151 </dependency> 5252 </dependencies> 5353 5454 <build> 5555 <plugins> 5656 <plugin> 5757 <groupId>org.springframework.boot</groupId> 5858 <artifactId>spring-boot-maven-plugin</artifactId> 5959 </plugin> 6060 </plugins> 6161 </build> 6262 6363 </project>
接着,我们先编写自定义的Realm类(MyJbdcRealm)
1 1 package com.hui.SpringBoot22.realm; 2 2 3 3 import org.apache.shiro.authc.*; 4 4 import org.apache.shiro.authz.AuthorizationException; 5 5 import org.apache.shiro.authz.AuthorizationInfo; 6 6 import org.apache.shiro.authz.SimpleAuthorizationInfo; 7 7 import org.apache.shiro.realm.AuthorizingRealm; 8 8 import org.apache.shiro.subject.PrincipalCollection; 9 9 import org.apache.shiro.util.ByteSource; 10 10 import org.apache.shiro.util.JdbcUtils; 11 11 12 12 import javax.sql.DataSource; 13 13 import java.sql.Connection; 14 14 import java.sql.PreparedStatement; 15 15 import java.sql.ResultSet; 16 16 import java.sql.SQLException; 17 17 import java.util.Arrays; 18 18 import java.util.Collection; 19 19 import java.util.LinkedHashSet; 20 20 import java.util.Set; 21 21 22 22 public class MyJdbcRealm extends AuthorizingRealm { 23 23 24 24 protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?"; 25 25 protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?"; 26 26 protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?"; 27 27 protected DataSource dataSource; 28 28 protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY; 29 29 protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY; 30 30 protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY; 31 31 protected boolean permissionsLookupEnabled = false; 32 32 33 33 public void setDataSource(DataSource dataSource) { 34 34 this.dataSource = dataSource; 35 35 } 36 36 37 37 public void setAuthenticationQuery(String authenticationQuery) { 38 38 this.authenticationQuery = authenticationQuery; 39 39 } 40 40 41 41 public void setUserRolesQuery(String userRolesQuery) { 42 42 this.userRolesQuery = userRolesQuery; 43 43 } 44 44 45 45 public void setPermissionsQuery(String permissionsQuery) { 46 46 this.permissionsQuery = permissionsQuery; 47 47 } 48 48 49 49 public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) { 50 50 this.permissionsLookupEnabled = permissionsLookupEnabled; 51 51 } 52 52 53 53 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 54 54 UsernamePasswordToken upToken = (UsernamePasswordToken) token; 55 55 String username = upToken.getUsername(); 56 56 if (username == null) { 57 57 throw new AccountException("Null usernames are not allowed by this realm."); 58 58 } 59 59 Connection conn = null; 60 60 SimpleAuthenticationInfo info = null; 61 61 try { 62 62 conn = dataSource.getConnection(); 63 63 String password = null; 64 64 password = getPasswordForUser(conn, username); 65 65 if (password == null) { 66 66 throw new UnknownAccountException("No account found for user [" + username + "]"); 67 67 } 68 68 info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName()); 69 69 } catch (SQLException e) { 70 70 final String message = "There was a SQL error while authenticating user [" + username + "]"; 71 71 throw new AuthenticationException(message, e); 72 72 } finally { 73 73 JdbcUtils.closeConnection(conn); 74 74 } 75 75 return info; 76 76 } 77 77 78 78 private String getPasswordForUser(Connection conn, String username) throws SQLException { 79 79 String result = null; 80 80 PreparedStatement ps = null; 81 81 ResultSet rs = null; 82 82 try { 83 83 ps = conn.prepareStatement(authenticationQuery); 84 84 ps.setString(1, username); 85 85 rs = ps.executeQuery(); 86 86 boolean foundResult = false; 87 87 while (rs.next()) { 88 88 if (foundResult) { 89 89 throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique."); 90 90 } 91 91 result = rs.getString(1); 92 92 foundResult = true; 93 93 } 94 94 } finally { 95 95 JdbcUtils.closeResultSet(rs); 96 96 JdbcUtils.closeStatement(ps); 97 97 } 98 98 return result; 99 99 } 100100 101101 @Override 102102 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 103103 if (principals == null) { 104104 throw new AuthorizationException("PrincipalCollection method argument cannot be null."); 105105 } 106106 String username = (String) getAvailablePrincipal(principals); 107107 Connection conn = null; 108108 Set<String> roleNames = null; 109109 Set<String> permissions = null; 110110 try { 111111 conn = dataSource.getConnection(); 112112 roleNames = getRoleNamesForUser(conn, username); 113113 if (permissionsLookupEnabled) { 114114 permissions = getPermissions(conn, username, roleNames); 115115 } 116116 } catch (SQLException e) { 117117 final String message = "There was a SQL error while authorizing user [" + username + "]"; 118118 throw new AuthorizationException(message, e); 119119 } finally { 120120 JdbcUtils.closeConnection(conn); 121121 } 122122 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames); 123123 info.setStringPermissions(permissions); 124124 return info; 125125 } 126126 127127 protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException { 128128 PreparedStatement ps = null; 129129 ResultSet rs = null; 130130 Set<String> roleNames = new LinkedHashSet<String>(); 131131 try { 132132 ps = conn.prepareStatement(userRolesQuery); 133133 ps.setString(1, username); 134134 rs = ps.executeQuery(); 135135 while (rs.next()) { 136136 String roleName = rs.getString(1); 137137 if (roleName != null) { 138138 roleNames.add(roleName); 139139 } 140140 } 141141 } finally { 142142 JdbcUtils.closeResultSet(rs); 143143 JdbcUtils.closeStatement(ps); 144144 } 145145 return roleNames; 146146 } 147147 148148 protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException { 149149 PreparedStatement ps = null; 150150 Set<String> permissions = new LinkedHashSet<String>(); 151151 try { 152152 ps = conn.prepareStatement(permissionsQuery); 153153 for (String roleName : roleNames) { 154154 ps.setString(1, roleName); 155155 ResultSet rs = null; 156156 try { 157157 rs = ps.executeQuery(); 158158 while (rs.next()) { 159159 String permissionString = rs.getString(1); 160160 String[] permissionNames = permissionString.split(","); 161161 permissions.addAll(Arrays.asList(permissionNames)); 162162 } 163163 } finally { 164164 JdbcUtils.closeResultSet(rs); 165165 } 166166 } 167167 } finally { 168168 JdbcUtils.closeStatement(ps); 169169 } 170170 return permissions; 171171 } 172172 }
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 1 package com.hui.SpringBoot22; 2 2 3 3 import com.hui.SpringBoot22.realm.MyJdbcRealm; 4 4 import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 5 5 import org.apache.shiro.mgt.SecurityManager; 6 6 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 7 7 import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 8 8 import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 9 9 import org.springframework.beans.factory.annotation.Autowired; 10 10 import org.springframework.beans.factory.annotation.Qualifier; 11 11 import org.springframework.context.annotation.Bean; 12 12 import org.springframework.context.annotation.Configuration; 13 13 import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; 14 14 15 15 import javax.sql.DataSource; 16 16 import java.util.*; 17 17 18 18 @Configuration 19 19 public class ShiroConfiguration { 20 20 21 21 @Autowired 22 22 private DataSource dataSource; 23 23 24 24 @Bean(name = "shiroFilter") 25 25 public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){ 26 26 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 27 27 shiroFilterFactoryBean.setSecurityManager(securityManager); 28 28 //配置login页、登陆成功页、没有权限页 29 29 shiroFilterFactoryBean.setLoginUrl("/"); 30 30 shiroFilterFactoryBean.setSuccessUrl("/index"); 31 31 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); 32 32 33 33 //配置访问权限(顺序执行拦截) 34 34 // “/**” 放到最下面,如果将("/**","authc")放到("/userLogin","anon")的上面 35 35 // 则“/userLogin”可能会被拦截 36 36 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); 37 37 filterChainDefinitionMap.put("/logout","logout"); 38 38 filterChainDefinitionMap.put("/userLogin","anon"); 39 39 filterChainDefinitionMap.put("/403","roles"); 40 40 filterChainDefinitionMap.put("/**","authc"); 41 41 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 42 42 return shiroFilterFactoryBean; 43 43 } 44 44 45 45 @Bean(name = "securityManager") 46 46 public SecurityManager securityManager(@Qualifier("myJdbcRealm")MyJdbcRealm myJdbcRealm){ 47 47 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 48 48 securityManager.setRealm(myJdbcRealm); 49 49 return securityManager; 50 50 } 51 51 52 52 @Bean(name = "myJdbcRealm") 53 53 public MyJdbcRealm myJdbcRealm(@Qualifier("credentialsMatcher") HashedCredentialsMatcher credentialsMatcher, 54 54 @Qualifier("dataSource") DataSource dataSource){ 55 55 MyJdbcRealm myJdbcRealm = new MyJdbcRealm(); 56 56 //打开shiro的权限 (默认为false) (不开启则不会检查权限 --> 点击“修改”,不管有没有权限都能进行跳转) 57 57 myJdbcRealm.setPermissionsLookupEnabled(true); 58 58 //设置datasource 59 59 myJdbcRealm.setDataSource(dataSource); 60 60 //设置密码加密器 61 61 myJdbcRealm.setCredentialsMatcher(credentialsMatcher); 62 62 //设置登陆验证sql语句 63 63 String sql = "select password from test_user where username = ?"; 64 64 myJdbcRealm.setAuthenticationQuery(sql); 65 65 //设置权限验证sql语句 66 66 String permissionSql = "select permission from permissions where role_name = ?"; 67 67 myJdbcRealm.setPermissionsQuery(permissionSql); 68 68 return myJdbcRealm; 69 69 } 70 70 71 71 //设置加密算法为MD5。加密次数为1 72 72 @Bean(name = "credentialsMatcher") 73 73 public HashedCredentialsMatcher credentialsMatcher(){ 74 74 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); 75 75 credentialsMatcher.setHashAlgorithmName("md5"); 76 76 credentialsMatcher.setHashIterations(1); 77 77 return credentialsMatcher; 78 78 } 79 79 80 80 /** 81 81 * 开启aop注解支持 -- 借助SpringAOP扫描使用shiro注解的类 82 82 * (不开启则不能扫描到shiro的@RequiresPermissions等注解) 83 83 * @param securityManager 84 84 * @return 85 85 */ 86 86 @Bean 87 87 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { 88 88 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); 89 89 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); 90 90 return authorizationAttributeSourceAdvisor; 91 91 } 92 92 93 93 //配置无权限异常处理,跳转到403 94 94 @Bean(name="simpleMappingExceptionResolver") 95 95 public SimpleMappingExceptionResolver 96 96 createSimpleMappingExceptionResolver() { 97 97 SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver(); 98 98 Properties mappings = new Properties(); 99 99 mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理 100100 mappings.setProperty("UnauthorizedException", "403"); 101101 r.setExceptionMappings(mappings); // None by default 102102 r.setDefaultErrorView("error"); // No default 103103 r.setExceptionAttribute("ex"); // Default is "exception" 104104 return r; 105105 } 106106 107107 }
上面代码中有几个需要注意的点:
第一点:是再定义的名为“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 1 package com.hui.SpringBoot22.pojo; 2 2 3 3 import javax.persistence.*; 4 4 5 5 @Entity 6 6 @Table(name = "test_user") 7 7 public class User { 8 8 @Id 9 9 @GeneratedValue(strategy = GenerationType.IDENTITY)//默认为AUTO,这里设置为自增 1010 private Long id; 1111 @Column(name = "username",length = 50) 1212 private String username; 1313 @Column(name = "password",length = 50) 1414 private String password; 1515 1616 public Long getId() { 1717 return id; 1818 } 1919 2020 public void setId(Long id) { 2121 this.id = id; 2222 } 2323 2424 public String getUsername() { 2525 return username; 2626 } 2727 2828 public void setUsername(String username) { 2929 this.username = username; 3030 } 3131 3232 public String getPassword() { 3333 return password; 3434 } 3535 3636 public void setPassword(String password) { 3737 this.password = password; 3838 } 3939 }
Role类
1 1 package com.hui.SpringBoot22.pojo; 2 2 3 3 import javax.persistence.*; 4 4 5 5 @Entity 6 6 @Table(name = "user_roles") 7 7 public class Role { 8 8 @Id 9 9 @GeneratedValue(strategy = GenerationType.IDENTITY) 1010 private Long id; 1111 @Column(name = "username",length = 50) 1212 private String username; 1313 @Column(name = "role_name",length = 50) 1414 private String roles; 1515 1616 public Long getId() { 1717 return id; 1818 } 1919 2020 public void setId(Long id) { 2121 this.id = id; 2222 } 2323 2424 public String getUsername() { 2525 return username; 2626 } 2727 2828 public void setUsername(String username) { 2929 this.username = username; 3030 } 3131 3232 public String getRoles() { 3333 return roles; 3434 } 3535 3636 public void setRoles(String roles) { 3737 this.roles = roles; 3838 } 3939 } 40 41Permission类 42 43 1 package com.hui.SpringBoot22.pojo; 44 2 45 3 import javax.persistence.*; 46 4 47 5 @Entity 48 6 @Table(name = "permissions") 49 7 public class Permission { 50 8 @Id 51 9 @GeneratedValue(strategy = GenerationType.IDENTITY) 5210 private Long id; 5311 @Column(name = "role_name",length = 50) 5412 private String roleName; 5513 @Column(name = "permission",length = 120) 5614 private String permissions; 5715 5816 public Long getId() { 5917 return id; 6018 } 6119 6220 public void setId(Long id) { 6321 this.id = id; 6422 } 6523 6624 public String getRoleName() { 6725 return roleName; 6826 } 6927 7028 public void setRoleName(String roleName) { 7129 this.roleName = roleName; 7230 } 7331 7432 public String getPermissions() { 7533 return permissions; 7634 } 7735 7836 public void setPermissions(String permissions) { 7937 this.permissions = permissions; 8038 } 8139 }
这里就不多说了,主要注意的就是对应的表名、字段名要与 ShiroConfiguration 类中的sql语句的表名、字段名一致。
接下来是Repository编写,直接看代码好了
1 1 package com.hui.SpringBoot22.repository; 2 2 3 3 import com.hui.SpringBoot22.pojo.User; 4 4 import org.springframework.data.jpa.repository.JpaRepository; 5 5 import org.springframework.data.jpa.repository.Modifying; 6 6 import org.springframework.data.jpa.repository.Query; 7 7 8 8 import javax.transaction.Transactional; 9 9 1010 public interface UserRepository extends JpaRepository<User,Long> { 1111 User findUserById(Long id); 1212 1313 @Transactional 1414 @Modifying 1515 @Query("update User set username=?2,password=?3 where id=?1") 1616 int updateUserById(Long id,String username,String password); 1717 1818 @Transactional 1919 @Modifying 2020 @Query("delete from User where id=?1") 2121 void deleteUserById(Long id); 2222 }
这里继承了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 1 package com.hui.SpringBoot22.controller; 2 2 3 3 import com.hui.SpringBoot22.pojo.User; 4 4 import com.hui.SpringBoot22.repository.UserRepository; 5 5 import com.hui.SpringBoot22.utils.Md5; 6 6 import org.apache.shiro.SecurityUtils; 7 7 import org.apache.shiro.authc.UsernamePasswordToken; 8 8 import org.apache.shiro.authz.annotation.RequiresPermissions; 9 9 import org.apache.shiro.subject.Subject; 1010 import org.springframework.beans.factory.annotation.Autowired; 1111 import org.springframework.stereotype.Controller; 1212 import org.springframework.web.bind.annotation.RequestMapping; 1313 1414 import java.util.List; 1515 import java.util.Map; 1616 1717 @Controller 1818 public class UserController { 1919 @Autowired 2020 private UserRepository userRepository; 2121 2222 @RequestMapping("/") 2323 public String toLogin(){ 2424 return "login"; 2525 } 2626 2727 @RequestMapping("/userLogin") 2828 public String userLogin(String username, String password, Map<String,Object> map){ 2929 Subject subject = SecurityUtils.getSubject(); 3030 UsernamePasswordToken token = new UsernamePasswordToken(username,password); 3131 try{ 3232 subject.login(token); 3333 map.put("loginName",username); 3434 }catch(Exception e){ 3535 map.put("msg","登陆失败"); 3636 return "login"; 3737 } 3838 return "forward:userList"; 3939 } 4040 4141 @RequestMapping("/userList") 4242 @RequiresPermissions("user:select") 4343 public String list(Map<String,Object> map){ 4444 List<User> users = userRepository.findAll(); 4545 map.put("userList",users); 4646 return "userList"; 4747 } 4848 4949 @RequestMapping("/toUserAdd") 5050 public String toAdd(){ 5151 return "userAdd"; 5252 } 5353 5454 @RequestMapping("/userAdd") 5555 @RequiresPermissions("user:add") 5656 public String userAdd(User user){ 5757 user.setPassword(Md5.md5(user.getPassword())); 5858 userRepository.save(user); 5959 return "forward:userList"; 6060 } 6161 6262 @RequestMapping("/toUserEdit") 6363 public String toEdit(Long id,Map<String,Object> map){ 6464 User user = userRepository.findUserById(id); 6565 map.put("user",user); 6666 return "userEdit"; 6767 } 6868 6969 @RequestMapping("/userEdit") 7070 @RequiresPermissions("user:update") 7171 public String userEdit(Long id,String username,String password){ 7272 password = Md5.md5(password); 7373 userRepository.updateUserById(id,username,password); 7474 return "forward:userList"; 7575 } 7676 7777 @RequestMapping("/userDelete") 7878 @RequiresPermissions("user:delete") 7979 public String deleteUser(Long id){ 8080 userRepository.deleteUserById(id); 8181 return "forward:userList"; 8282 } 8383 }
@RequiresPermissions注解是验证用户权限
@RequiresRoles注解是验证用户角色(这个在RoleController中用到,这里没有贴出来)
然后,再看一部分使用 thymeleaf 的HTML代码
1 1 <!DOCTYPE html> 2 2 <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> 3 3 <head> 4 4 <meta charset="UTF-8"> 5 5 <title>Title</title> 6 6 </head> 7 7 <body> 8 8 <div style="margin-left: 30%"> 9 9 <form action="/roleUpdate" method="post"> 1010 <input type="hidden" name="id" th:value="${role.id}"/> 1111 用 户 名:<input name="username" type="text" th:value="${role.username}"/><br/><br/> 1212 选择角色:<input style="margin-left: 8px;" type="radio" name="roles" 1313 th:each="roleName,roleNameStat:${roleNameList}" 1414 th:value="${roleName}" 1515 th:text="${roleName}" 1616 th:attr="checked=${roleName==role.roles?true:false}" 1717 /><br/><br/> 1818 <input type="submit" value="提交"/> 1919 </form> 2020 </div> 2121 </body> 2222 </html>
老实说,第一次使用 thymeleaf 真的不大习惯,总是写成常规的 HTML 代码
上面也没什么好说的,也就一个下拉框的遍历(
th:text 表示文本值, th:value 表示value值,th:attr 表示是否选中状态,
th:each 就是遍历后台传来的 list 集合 --> roleName 代表每一个 list 集合元素,roleNameStat.index 代表着该元素的下标
)
最后,再看一下配置文件 application.properties
1 1 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 2 2 spring.datasource.url=jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC 3 3 spring.datasource.username=xxx 4 4 spring.datasource.password=xxx 5 5 6 6 spring.jpa.hibernate.ddl-auto=create-drop 7 7 spring.jpa.show-sql=true 8 8 spring.jpa.database=mysql 9 9 1010 spring.thymeleaf.cache=false 1111 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