本案例是通过使用SpringSecurity来实现通过读取数据库中的数据,来完成认证授权的案例。
1. 向数据库中添加具体实例
创建出五个表,五个表之间的关系为:
sys_user:登录表,用于登陆后查询id
sys_user_role:用于根据用用户的id来查询到role_ id
sys_role_function:用于通过role_id来查询对应的fun_id
sys_function:用于通过fun_id来查询对应的方法,以及fun_code方法权限
DROP TABLE IF EXISTS `sys_function`;
CREATE TABLE `sys_function` (
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`fun_name` varchar(50) DEFAULT NULL,
`fun_url` varchar(50) DEFAULT NULL,
`fun_code` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- ----------------------------
-- Records of sys_function
-- ----------------------------
INSERT INTO `sys_function` VALUES ('1', '商品列表查询', '/goods/list', 'goods:list');
INSERT INTO `sys_function` VALUES ('2', '商品添加', '/goods/add', 'goods:add');
INSERT INTO `sys_function` VALUES ('3', '商品删除', '/goods/del', 'goods:del');
INSERT INTO `sys_function` VALUES ('4', '商品更新', '/goods/update', 'goods:update');
-- ----------------------------
-- Table structure for `sys_role`
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rname` varchar(50) DEFAULT NULL,
`rcode` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`rdesc` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '管理员', 'admin', null);
INSERT INTO `sys_role` VALUES ('2', '普通用户', 'user', null);
-- ----------------------------
-- Table structure for `sys_role_function`
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_function`;
CREATE TABLE `sys_role_function` (
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`role_id` varchar(50) DEFAULT NULL,
`fun_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- ----------------------------
-- Records of sys_role_function
-- ----------------------------
INSERT INTO `sys_role_function` VALUES ('1', '1', '1');
INSERT INTO `sys_role_function` VALUES ('2', '1', '2');
INSERT INTO `sys_role_function` VALUES ('3', '1', '3');
INSERT INTO `sys_role_function` VALUES ('4', '1', '4');
INSERT INTO `sys_role_function` VALUES ('5', '2', '1');
-- ----------------------------
-- Table structure for `sys_user`
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`uname` varchar(30) DEFAULT NULL,
`upwd` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`uemail` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`uphone` char(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('a', 'admin', '$2a$10$tUBtEISZ/BkzQl0IJ6faOOZf6E1udcN9NsALobz2ofqooUPKUbm0K', null, '15516197242');
INSERT INTO `sys_user` VALUES ('b', 'anne', '$2a$10$tUBtEISZ/BkzQl0IJ6faOOZf6E1udcN9NsALobz2ofqooUPKUbm0K', null, '15516197241');
-- ----------------------------
-- Table structure for `sys_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_id` varchar(50) DEFAULT NULL,
`role_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', 'a', '1');
INSERT INTO `sys_user_role` VALUES ('2', 'b', '2');
2.项目的具体结构
3.添加相关的依赖,主要使用SpringBoot以及Mybatis_Plus,以及application.yml配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version> 3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
application.yml:
server:
port: 9007
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/security_db?serverTimezone=Asia/Shanghai
username: root
password: 123456
# 连接池的类型
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
max-active: 100
min-idle: 10
4 .主要代码理解
①:项目中的mapper层,entiity层,service层以及实现类是通过mybatis_plus的相关注解,不再赘述。
②:创建对应的CusUserDetailsServiceImpl实现类,LoginUser实体类来完成对数据库中数据的读取。
LoginUser实体类:
该实体类要实现UserDetails的接口,来实现里面的方法,getAuthorities()方法主要是获取授权的权限:SimpleGrantedAuthority是查看GrantedAuthority的具体实现类来写的。(查看底层的逻辑)。
ArrayList<SimpleGrantedAuthority> strings = new ArrayList<>();
for (Function function : functionList) {
strings.add(new SimpleGrantedAuthority(function.getFunCode()));
}
return strings;
public class LoginUser implements UserDetails {
private User user;
private List<Function> functionList;
public LoginUser(User user, List<Function> functionList) {
this.user = user;
this.functionList = functionList;
}
/**
* 获取登录的用户 拥有哪些权限
*
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<SimpleGrantedAuthority> strings = new ArrayList<>();
for (Function function : functionList) {
strings.add(new SimpleGrantedAuthority(function.getFunCode()));
}
return strings;
}
@Override
public String getPassword() {
return user.getUpwd();
}
@Override
public String getUsername() {
return user.getUname();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
CusUserDetailsServiceImpl实现类:
在这个类中,通过我们数据库中表的相关逻辑在实现类中实现相对应的方法。来查询我们所需要的loginUser。
@Service
public class CusUserDetailsServiceImpl implements UserDetailsService {
@Resource
private UserService userService;
@Resource
private UserRoleService userRoleService;
@Resource
private RoleFunctionService roleFunctionService;
@Resource
private FunctionService functionService;
/**
* 从何处 去加载一个用户
*
* @param s
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userService.getUserByName(s);
String userId = user.getId();
// 根据用户id 去查询 角色
List<String> roleIds = userRoleService.getRoleByUserId(userId);
// 根据roleId 去找 functionId
List<String> functionIds = roleFunctionService.getFunctionByRoleIds(roleIds);
// 根据functionId 去找function
List<Function> functions = functionService.listByIds(functionIds);
//
LoginUser loginUser = new LoginUser(user, functions);
return loginUser;
}
}
③:完成SpringSecurity的配置类:
我们是基于session来实现的。前端使用postman来返回表单的数据。
SecurityConfig.java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,jsr250Enabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
public NamePassFilter namePassFilter() throws Exception {
NamePassFilter namePassFilter = new NamePassFilter();
//交给拦截管理器来进行处理
namePassFilter.setAuthenticationManager(authenticationManagerBean());
//设置拦截路径为/login
namePassFilter.setFilterProcessesUrl("/login");
// 认证成功
namePassFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("status", "200");
hashMap.put("msg", "认证成功");
hashMap.put("auth", authentication);
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(hashMap));
}
});
// 认证失败
namePassFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write("认证失败了" + e.getMessage());
}
});
return namePassFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.addFilterAt(namePassFilter(), UsernamePasswordAuthenticationFilter.class);
http.csrf().disable();
}
}
5.验证:
6:项目完整代码:
gitee:https://gitee.com/wangdaxia0089/spring-security-demo