1、创建需要的数据库
1.1用户表
CREATE TABLE user (
uid int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
password varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (uid) USING
BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8
COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.2权限表
CREATE TABLE role (
rid int(11) NOT NULL AUTO_INCREMENT,
role varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (rid) USING BTREE ) ENGINE = InnoDB
AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci
ROW_FORMAT = Dynamic;
1.3关系表
CREATE TABLE user_role (
id int(11) NOT NULL AUTO_INCREMENT,
uid int(11) NULL DEFAULT NULL, rid int(11) NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 10
CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
1.4添加测试数据
-- 添加2个用户
INSERT INTO user VALUES (1, 'user', 'e10adc3949ba59abbe56e057f20f883e');
INSERT INTO user VALUES (2, 'admin', 'e10adc3949ba59abbe56e057f20f883e');
-- 添加2个角色
INSERT INTO role VALUES (1, 'user');
INSERT INTO role VALUES (2, 'admin');
-- 2个用户,分别拥有1个角色
INSERT INTO user_role VALUES (1, 1, 1);
INSERT INTO user_role VALUES (2, 2, 2);
2、创建一个springboot项目,名字随便,然后在项目的pom.xml中添加如下有关依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--springboot-spring-security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!--mybatis和springboot整合包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!--jdbc驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--log4j-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<resources>
<!-- 配置将哪些资源文件(静态文件/模板文件/mapper文件)加载到tomcat输出目录里 -->
<resource>
<!--java文件的路径-->
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
<!-- <filtering>false</filtering>-->
</resource>
<resource>
<directory>src/main/resources</directory><!--资源文件的路径-->
<includes>
<include>**/*.*</include>
</includes>
<!-- <filtering>false</filtering>-->
</resource>
</resources>
</build>
3、创建项目结构包
4、在application-db.yml配置数据源
spring:
datasource:
username: root
password: 123456
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
5、在application.properties做有关配置
#端口
server.port=8081
#添加application-db.yml
spring.profiles.active=db
#为实体类设置别名
mybatis.type-aliases-package=com.itlhc.pojo
#绑定mapper.xml
mybatis.mapper-locations=com/itlhc/dao/*.xml
6、在dao包下创建UserMapper接口和UserMapper.xml配置文件
@Mapper
@Repository
public interface UserMapper {
//添加用户
@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
int insertUser(User user);
//添加用户权限
@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
Integer insertsqx(User qx);
//查询全部用户
List<User> selectUser();
//根据用户名字查询用户
UserDTO selectUserByUsername(@Param("username") String username);
//根据用户名查id
User selectUsername(User user);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itlhc.dao.UserMapper">
<resultMap id="userRoleMap" type="com.itlhc.pojo.UserDTO">
<id property="uid" column="uid"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<collection property="roles" ofType="com.itlhc.pojo.RoleDTO">
<id property="rid" column="rid"/>
<result property="role" column="role"></result>
</collection>
</resultMap>
<!--增加用户-->
<insert id="insertUser" parameterType="User">
insert into user(username,password)values(#{username},#{password});
</insert>
<!--添加用户权限-->
<select id="insertsqx" parameterType="User">
insert into user_role(id,uid,rid)values (#{id},#{uid},2);
</select>
<!--根据用户名查询id-->
<select id="selectUsername" parameterType="User" resultType="User">
select * from user where username = #{username};
</select>
<!--查询全部用户-->
<select id="selectUser" resultMap="userRoleMap">
select uid,username,password from user;
</select>
<!--验证用户密码和查询用户权限-->
<select id="selectUserByUsername" resultMap="userRoleMap">
select user.uid, user.username, user.password, role.rid, role.role
from user, user_role, role
where user.username=#{username}
and user.uid = user_role.uid
and user_role.rid = role.rid
</select>
</mapper>
7、在pojo包下创建如下实体类
7.1Role
//权限表映射
@Data
public class Role {
private Integer rid;
private String role;
}
7.2RoleDTO
//权限匹配
@Setter
@Getter
public class RoleDTO extends Role {
private Set<Role> users;
}
7.3TestUser
//权限和密码认证
public class TestUser extends User {
public TestUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
}
7.4User
//用户表映射
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private Integer id;//关系表id
private Integer rid;//权限id
private Integer uid;//用户id
private String username;
private String password;
}
7.5UserDTO
//用户信息以及权限匹配
@Setter
@Getter
public class UserDTO extends User {
private Set<Role> roles;
}
8、在service中创建UserService接口和UserServiceImpl实现类,以及MyUserDetailsService身份认证类
8.1UserService
public interface UserService {
//添加用户
int insertUser(User user);
//添加用户权限
Integer insertsqx(User qx);
//查询全部用户
List<User> selectUser();
//查询用户
UserDTO selectUserByUsername(@Param("username") String username);
}
8.2UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int insertUser(User user) {
//加密
user.setUsername(user.getUsername());
user.setPassword(MD5Util.pwdMd5(user.getPassword()));
return userMapper.insertUser(user);
}
@Override
public Integer insertsqx(User qx) {
User user = userMapper.selectUsername(qx);
Integer qxs = null;
qxs = user.getUid();
qx.setUid(qxs);
userMapper.insertsqx(qx);
System.out.println("qxs:"+qx);
return null;
}
@Override
public List<User> selectUser() {
return userMapper.selectUser();
}
@Override
public UserDTO selectUserByUsername(String username) {
return userMapper.selectUserByUsername(username);
}
}
8.3MyUserDetailsService
//身份认证类
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("username:"+username);
UserDTO user = userService.selectUserByUsername(username);
System.out.println("user:"+user);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
//添加用户拥有的多个角色
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
Set<Role> roles = user.getRoles();
for (Role role : roles) {
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRole()));
}
System.out.println("grantedAuthorities:"+grantedAuthorities);
System.out.println("password:"+user.getPassword());
//构造返回值
TestUser testUser = new TestUser(user.getUsername(), user.getPassword(), grantedAuthorities);
return testUser;
}
}
9、在utils工具包下创建MD5Util加密类
/**
* MD5加密解密工具类
*/
public class MD5Util {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"};
/**
* 转换字节数组为16进制字串
*
* @param b 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
}
/**
* 转换byte到16进制
*
* @param b 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* 密码加密
*
* @param password
* @return
*/
public static String pwdMd5(String password) {
String pwdMd5 = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(password.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
pwdMd5 = sb.toString().toLowerCase();
} catch (Exception e) {
e.printStackTrace();
}
return pwdMd5;
}
}
10、在config包下创建SecurityConfig配置类
/**
* 开启注解权限鉴定, securedEnabled: 开启@Secured注解(拥有某个角色可以访问);
* prePostEnabled:开启@PreAuthorize注解(拥有某个角色或者某个权限可以访问,在方法执行之前校验)
* 开启@PostAuthorize注解(拥有某个角色或者某个权限可以访问,在方法执行之后校验)
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
/**
* 指定加密方式-->BCryptPasswordEncoder
*/
/*@Bean
public PasswordEncoder passwordEncoder(){
// 使用BCrypt加密密码
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
// 从数据库读取的用户进行身份认证
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}*/
/**
* 指定加密方式-->MD5Util
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
//密码加密方法
.passwordEncoder(new PasswordEncoder() {
//密码加密规则
@Override
public String encode(CharSequence charSequence) {
return MD5Util.pwdMd5((String) charSequence);
}
//密码校验规则
@Override
public boolean matches(CharSequence charSequence, String s) {
return encode(charSequence).equals(s);
}
});
}
/*
hasAuthority没ROLE_ 前缀,hasAnyRole有ROLE_ 前缀,是自动添加ROLE_前缀。
authority 描述的的是一个具体的权限,例如针对某一项数据的查询或者删除权限,
它是一个 permission,例如 read_employee、delete_employee、update_employee 之类的,这些都是具体的权限,相信大家都能理解。
hasAnyRole在项目中,我们可以将用户和角色关联,角色和权限关联,权限和资源关联。
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//禁用跨域保护
http.csrf().disable();
//配置登出
http.logout().logoutUrl("/logout").logoutSuccessUrl("/LoginHtml").permitAll();
http
.formLogin()//自定义自己编写的登陆页面
.loginPage("/LoginHtml") //登录页面设置
.loginProcessingUrl("/user/login")//登录访问路劲(随便写)
.defaultSuccessUrl("/index1").permitAll()//登录成功之后跳转的页面
.and().authorizeRequests()
.antMatchers("/", "/user/login", "/LoginHtml","/registerUser","/registers").permitAll()//哪些路径可以直接访问,不需要验证
.antMatchers("/test2").hasAnyRole("user")//拥有该权限可以访问,hasAnyRole会自动添加ROLE_前缀
.antMatchers("/test3").hasAnyRole("admin")
;
//没有权限进行访问跳转的页面
http.exceptionHandling().accessDeniedPage("/unauth");
}
}
11、在controller包下创建LoginController登录控制类、RegisterController注册控制类、UserController用户控制类
11.1LoginController
@Controller
public class LoginController {
@GetMapping("test2")
@ResponseBody
public String test2(){
return "hello,test2";
}
@GetMapping("test3")
@ResponseBody
public String test3(){
return "hello,test3";
}
@GetMapping("index1")
public String index(){
return "user/index1";
}
@RequestMapping("LoginHtml")
public String loginHtml(){
return "user/login";
}
@GetMapping("unauth")
public String unauthHtml(){
return "user/unauth";
}
}
11.2RegisterController
@Controller
public class RegisterController {
@Autowired
private UserService userService;
@RequestMapping("registers")
public String registers(){
return "user/register";
}
@RequestMapping("registerUser")
public String register(User user){
int i = userService.insertUser(user);
userService.insertsqx(user);
System.out.println("注册成功:"+i);
return "user/login";
}
}
11.3UserController
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/selectUser")
@ResponseBody
public List<User> selectUserList(){
List<User> users = userService.selectUser();
return users;
}
}
12、html页面
12.1index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎来到lhc信息管理系统</h1>
<a th:href="@{/LoginHtml}">登录</a>
<a th:href="@{/registers}">注册</a>
</body>
</html>
12.2login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎登录!</h1>
<form th:action="@{/user/login}" th:method="post">
<div class="form-group">
<label for="username">Username</label>
<input type="username" class="form-control" id="username" th:name="username" placeholder="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" th:name="password" placeholder="password">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</body>
</html>
12.3register.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎注册!</h1>
<form th:action="@{/registerUser}" th:method="post">
<div class="form-group">
<label for="username">Username</label>
<input type="username" class="form-control" id="username" th:name="username" placeholder="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" th:name="password" placeholder="password">
</div>
<button type="submit" class="btn btn-default">注册</button>
</form>
</body>
</html>
12.4index1.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!--<input id="text" type="button" value="查询"/>-->
<!--<p>这是一个段落。</p>
<button>切换</button>-->
<div class="row clearfix">
<div class="col-md-12 column">
<table id = id="table" class="table table-hover table-striped">
<thead>
<tr>
<th><input type="checkbox" id="checkAll" name="checkAll">全选</th>
<th>uid</th>
<th>username</th>
<th>password</th>
<th colspan="2">操作</th>
</tr>
</thead>
<tbody id="tbodyId">
<tr>
</tr>
</tbody>
</table>
</div>
</div>
<a th:href="@{/logout}">注销</a>
<a th:href="@{/test2}">test2</a>
<a th:href="@{/test3}">test3</a>
</body>
<script type="text/javascript" src="https://upcdn.b0.upaiyun.com/libs/jquery/jquery-2.0.2.min.js"></script>
<script type="text/javascript">
/*$(document).ready(function(){
$("button").click(function(){
$("p").slideToggle();
});
});
*/
$(document).ready(function(){
/*$("#text").bind("click",function(){*/
// 基于ajax技术向服务端发送异步请求, 获取数据, 然后更新到页面上
// 1. 定义url
var url = "selectUser";
// 2. 请求参数
// 3. 发送异步请求, 处理响应结果
// 向服务器发送Get请求
$.get(url, function(result) { // 用于处理结果
// 获取tbody对象
var tBody = $("#tbodyId");
// 清空tbody的内容
tBody.empty();
// 迭代result对象, 将数据填充到tbody
result.forEach(user => {
tBody.append(doCreateRow(user));
});
}, "json");
// 创建一行数据
function doCreateRow(user) {
return `<tr>
<td> <input type='checkbox' id="checkAll" name="checkAll" value=${user.uid}> </td>
<td> ${user.uid} </td>
<td> ${user.username} </td>
<td> ${user.password} </td>
<td>
<button type='button' class='btn btn-danger btn-sm' onclick='doDeleteById(${user.uid})'>update</button>
<button type='button' class='btn btn-danger btn-sm' onclick='doDeleteById(${user.uid})'>delete</button>
</td>
</tr>`;
}
/* });*/
});
</script>
</html>
12.5error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>发生错误</h1>
</body>
</html>
12.6unauth.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>没有权限</h1>
</body>
</html>