从登录开始
原创大约 5 分钟
Spring Security是一个重量级的RBAC权限管理框架,当SpringMVC还是主流开发框架的时候,因为它的配置比较繁琐,所以事实上Shiro用得更多。
但Springboot给Spring Security提供了更友好地配置方式,所以Spring Security又重新被重视起来。
Spring Security的核心功能主要就是三个。
认证
(Who
)授权
(What
)防伪
Spring Security本质上是由一组Filter
(过滤器)组成的FilterChain
(过滤器链),FilterChain
中的每个Filter
都专注于自身的职责,组合在一起实现所有与权限相关的功能。
先从登录
功能开始。
在IDEA上创建一个新的Springboot项目。

然后创建需要用到的相关数据表。
DROP TABLE IF EXISTS sys_user;
CREATE TABLE sys_user (
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) NOT NULL,
password varchar(255) NOT NULL,
createtime datetime NOT NULL,
updatetime datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT='用户表';
DROP TABLE IF EXISTS sys_role;
CREATE TABLE sys_role (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(255),
createtime datetime NOT NULL,
updatetime datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT='角色表';
DROP TABLE IF EXISTS sys_user_role;
CREATE TABLE sys_user_role (
userid int(11) NOT NULL,
roleid int(11) NOT NULL,
createtime datetime NOT NULL,
updatetime datetime NOT NULL,
PRIMARY KEY (userid, roleid)
) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT='用户角色表';
-- 初始化数据
INSERT INTO sys_user VALUES (1, 'admin', '123456', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_user VALUES (2, 'manager', '123456', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_user VALUES (3, 'employee', '123456', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_role VALUES (1, 'ROLE_ADMIN', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_role VALUES (2, 'ROLE_MANAGER', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_role VALUES (3, 'ROLE_EMPLOYEE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_user_role VALUES (1, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_user_role VALUES (2, 2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO sys_user_role VALUES (3, 3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
修改pom.xml
文件。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 加入exclusions -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 加入commons-lang3和fastjson -->
<!-- apache commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- alibaba -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
</dependency>
<!-- 去掉spring-boot-starter-test和spring-security-test -->
增加application.properties
配置项。
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
可以用JDBC或者MyBatis作持久化,这里用的是JDBC。
package com.xiangwang.vmall.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* MySQLDao
*
*/
@Component
public class MySQLDao<T> {
@Autowired
private JdbcTemplate jdbcTemplate;
// 创建数据
public int create(final String sql, final @Nullable Object... args) throws Exception {
try {
if (1 <= jdbcTemplate.update(sql, args)) {
return 0;
}
return -1;
} catch (DuplicateKeyException e) {
e.printStackTrace();
throw new DuplicateKeyException("data duplicate exception");
} catch (DataAccessException e) {
e.printStackTrace();
throw new RuntimeException("create data exception");
}
}
// 查询数量
public Integer count(final String sql, final Object[] args) {
try {
return jdbcTemplate.queryForObject(sql, args, Integer.class);
} catch (DataAccessException e) {
e.printStackTrace();
}
return null;
}
// 查询单条数据
public Object findOne(final String sql, final RowMapper<?> rowMapper, final @Nullable Object... args) {
try {
List<?> list = jdbcTemplate.query(sql, rowMapper, args);
if (null != list && list.size() > 0) {
return list.get(0);
}
} catch (DataAccessException e) {
e.printStackTrace();
}
return null;
}
// 获得列表
public List<?> find(final String sql, final RowMapper<?> rowMapper, final @Nullable Object... args) {
try {
List<?> list = jdbcTemplate.query(sql, rowMapper, args);
if (null != list && 0 != list.size()) {
return list;
}
} catch (DataAccessException e) {
e.printStackTrace();
}
return null;
}
// 更新或删除数据
public boolean update(final String sql, final @Nullable Object... args) throws Exception {
try {
return 0 <= jdbcTemplate.update(sql, args);
} catch (DataAccessException e) {
e.printStackTrace();
throw new RuntimeException("update or remove object exception");
}
}
}
创建实体类(仅以user
类为例)。
package com.xiangwang.vmall.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.jdbc.core.RowMapper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
* 用户entity
*
*/
public class SysUser implements Serializable, RowMapper<SysUser> {
private static final long serialVersionUID = -1214743110268373599L;
private int id;
private String username;
private String password;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
protected Date createtime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
protected Date updatetime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonIgnore
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
public Date getUpdatetime() {
return updatetime;
}
public void setUpdatetime(Date updatetime) {
this.updatetime = updatetime;
}
@Override
public SysUser mapRow(ResultSet result, int i) throws SQLException {
SysUser user = new SysUser();
user.setId(result.getInt("id"));
user.setUsername(result.getString("username"));
user.setPassword(result.getString("password"));
user.setCreatetime(result.getTimestamp("createtime"));
user.setUpdatetime(result.getTimestamp("updatetime"));
return user;
}
}
创建三个服务类,分别是用户、角色和用户角色。
/**
* 用户Service
*
*/
@Service
public class UserService {
@Autowired
private MySQLDao mySQLDao;
public Integer count() {
String sql = "SELECT COUNT(id) FROM sys_user;";
return mySQLDao.count(sql, new Object[] {});
}
public int save(String username, String password) throws Exception {
String sql = "INSERT INTO sys_user(username, password) VALUES (?, ?);";
return mySQLDao.create(sql, username, password);
}
public SysUser getById(int id) {
String sql = "SELECT id, username, password, createtime, updatetime FROM sys_user WHERE id = ?";
return (SysUser) mySQLDao.findOne(sql, new SysUser(), id);
}
public SysUser getByName(String username) {
String sql = "SELECT id, username, password, createtime, updatetime FROM sys_user WHERE username = ?";
return (SysUser) mySQLDao.findOne(sql, new SysUser(), username);
}
public List<SysUser> getAll() {
String sql = "SELECT id, username, password, createtime, updatetime FROM sys_user";
return mySQLDao.find(sql, new SysUser());
}
}
/**
* 角色Service
*
*/
@Service
public class RoleService {
@Autowired
private MySQLDao roleDao;
public void save(String name) throws Exception {
String sql = "INSERT INTO sys_role(name) VALUES (?);";
roleDao.create(sql, name);
}
public SysRole getById(int id) {
String sql = "SELECT id, name, createtime, updatetime FROM sys_role WHERE id = ?";
return (SysRole) roleDao.findOne(sql, new SysRole(), id);
}
public SysRole getByName(String name) {
String sql = "SELECT id, name, createtime, updatetime FROM sys_role WHERE name = ?";
return (SysRole) roleDao.findOne(sql, new SysRole(), name);
}
public List<SysRole> getAll() {
String sql = "SELECT id, name, createtime, updatetime FROM sys_role";
return roleDao.find(sql, new SysRole());
}
public List<SysRole> getByUserId(int userid) {
String sql = "SELECT r.id, r.name, r.createtime, r.updatetime " +
"FROM sys_role AS r, sys_user_role AS ur, sys_user AS u " +
"WHERE u.id = ? AND u.id = ur.userid AND ur.roleid = r.id";
return (List<SysRole>) roleDao.findOne(sql, new SysRole(), userid);
}
}
/**
* 用户角色Service
*
*/
@Service
public class UserRoleService {
@Autowired
private MySQLDao mySQLDao;
public void save(int userid, int roleid) throws Exception {
String sql = "INSERT INTO sys_user_role(userid, roleid) VALUES (?, ?);";
mySQLDao.create(sql, userid, roleid);
}
public List<SysUserRole> getByUserId(int userid) {
String sql = "SELECT userid, roleid, createtime, updatetime FROM sys_user_role WHERE userid = ?";
return mySQLDao.find(sql, new SysUserRole(), userid);
}
public List<SysUserRole> getByRoleId(int roleid) {
String sql = "SELECT userid, roleid, createtime, updatetime FROM sys_user_role WHERE roleid = ?";
return mySQLDao.find(sql, new SysUserRole(), roleid);
}
public SysUserRole getById(int userid, int roleid) {
String sql = "SELECT userid, roleid, createtime, updatetime FROM sys_user_role WHERE userid = ? AND roleid = ?";
return (SysUserRole) mySQLDao.findOne(sql, new SysUserRole(), userid, roleid);
}
}
创建控制器类。
/**
* 登录Controller
*
*/
@RestController
public class LoginController {
@GetMapping("/")
public String index() {
final String username = SecurityContextHolder.getContext().getAuthentication().getName();
System.out.println("当前登录用户:" + username);
return "SUCCESS";
}
// 登录
@PostMapping("/login")
public String login(String username, String password) {
System.out.println("当前登录用户:" + username);
return "SUCCESS";
}
// 登出
@GetMapping("/logout")
public String logout(String username) {
System.out.println("登出用户:" + username);
return "SUCCESS";
}
}
启动服务,然后用Postman访问接口。

调用/login?username=user&password=123456
接口之后,发现什么都没有显示。
这是因为还没有配置Spring Security。
感谢支持
更多内容,请移步《超级个体》。