首先,添加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
当然,你也可以在自定义的 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