一:依赖坐标
<!--springboot整合SpringSecurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
导入SpringSecurity坐标,启动项目便会出现权限控制的登录界面,默认用户名为user,默认密码会在控制台打印,输入账号密码即可实现登录访问;
如需修改登录的账号密码只需要在配置文件做如下配置即可实现:
spring.security.user.name=fanaozhe
spring.security.user.password=123
二:数据库
实现一个简单的动态权限控制,总共涉及5张表,分别为user(用户)、role(角色)、user_role(用户角色关系)、menu(资源)、menu_role(资源角色);
user表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ruser
-- ----------------------------
DROP TABLE IF EXISTS `ruser`;
CREATE TABLE `ruser` (
`id` int(11) NOT NULL,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`enabled` tinyint(1) NULL DEFAULT NULL COMMENT '当前账号是否可用',
`locked` tinyint(1) NULL DEFAULT NULL COMMENT '当前账号是否锁定',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of ruser
-- ----------------------------
INSERT INTO `ruser` VALUES (1, 'root', '123', 1, 0);
INSERT INTO `ruser` VALUES (2, 'admin', '123', 1, 0);
INSERT INTO `ruser` VALUES (3, 'fanaozhe', '123', 1, 0);
SET FOREIGN_KEY_CHECKS = 1;
role表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`nameZh` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_dba', '数据库管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户');
SET FOREIGN_KEY_CHECKS = 1;
user_role表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ruser_role
-- ----------------------------
DROP TABLE IF EXISTS `ruser_role`;
CREATE TABLE `ruser_role` (
`id` int(11) NOT NULL,
`uid` int(11) NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of ruser_role
-- ----------------------------
INSERT INTO `ruser_role` VALUES (1, 1, 1);
INSERT INTO `ruser_role` VALUES (2, 1, 2);
INSERT INTO `ruser_role` VALUES (3, 2, 2);
INSERT INTO `ruser_role` VALUES (4, 3, 3);
SET FOREIGN_KEY_CHECKS = 1;
menu表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`id` int(11) NOT NULL,
`pattern` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '/student/**');
INSERT INTO `menu` VALUES (2, '/book/**');
INSERT INTO `menu` VALUES (3, '/user/**');
SET FOREIGN_KEY_CHECKS = 1;
menu_role表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for menu_role
-- ----------------------------
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role` (
`id` int(11) NOT NULL,
`mid` int(11) NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu_role
-- ----------------------------
INSERT INTO `menu_role` VALUES (1, 1, 1);
INSERT INTO `menu_role` VALUES (2, 2, 2);
INSERT INTO `menu_role` VALUES (3, 3, 3);
SET FOREIGN_KEY_CHECKS = 1;
三:配置文件
WebSecurityConfig :
package top.fanaozhe.springbootdemo.config.securityconfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import top.fanaozhe.springbootdemo.service.RuserService;
/**
* @author faz
* @create 2020-10-24-14:32
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
RuserService ruserService;
/* @Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}*/
@Bean
PasswordEncoder PasswordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//设置角色的继承关系
@Bean
RoleHierarchy roleHierarchy(){
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy ="ROLE_dba > ROLE_admin > ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(ruserService);
}
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O object) {
object.setSecurityMetadataSource(cfisms());
object.setAccessDecisionManager(cadm());
return object;
}
})
//登录页面
.and()
.formLogin()
.loginProcessingUrl("/login").permitAll()
.and()
.csrf().disable();
}
@Bean
CustomFilterInvocationSecurityMetadataSource cfisms(){
return new CustomFilterInvocationSecurityMetadataSource();
}
CustomAccessDecisionManager cadm(){
return new CustomAccessDecisionManager();
}
/*//基于内存的配置;
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/book/**").hasRole("admin")
.antMatchers("/student/**").hasRole("dba")
.antMatchers("/user/**").hasRole("user")
//其他用户访问必须经过认证;
.anyRequest()
.authenticated()
//登录页面
.and()
.formLogin()
.loginProcessingUrl("/login").permitAll()
.and()
.csrf().disable();
}*/
}
CustomFilterInvocationSecurityMetadataSource:
package top.fanaozhe.springbootdemo.config.securityconfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import top.fanaozhe.springbootdemo.entity.Menu;
import top.fanaozhe.springbootdemo.entity.Role;
import top.fanaozhe.springbootdemo.mapper.MenuMapper;
import java.util.Collection;
import java.util.List;
/**
* @author faz
* @create 2020-10-26-19:35
*/
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Autowired
MenuMapper menuMapper;
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
List<Menu> allMenus = menuMapper.getAllMenus();
for (Menu menu : allMenus) {
if (antPathMatcher.match(menu.getPattern(), requestUrl)) {
List<Role> roles = menu.getRoles();
String[] roleArr = new String[roles.size()];
for (int i = 0; i < roleArr.length; i++) {
roleArr[i] = roles.get(i).getName();
}
return SecurityConfig.createList(roleArr);
}
}
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
CustomAccessDecisionManager :
package top.fanaozhe.springbootdemo.config.securityconfig;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* @author faz
* @create 2020-10-26-20:02
*/
public class CustomAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication auth, Object object,
Collection<ConfigAttribute> ca) throws AccessDeniedException, InsufficientAuthenticationException {
Collection<? extends GrantedAuthority> auths = auth.getAuthorities();
for (ConfigAttribute configAttribute:ca){
if("ROLE_LOGIN".equals(configAttribute.getAttribute())&& auth instanceof UsernamePasswordAuthenticationToken){
return;
}
for(GrantedAuthority authority:auths){
if(configAttribute.getAttribute().equals(authority.getAuthority())){
return;
}
}
}
throw new AccessDeniedException("权限不足");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
四:代码实现
@Mapper
public interface RuserMapper {
/*通过用户名查找用户*/
Ruser findUserByUsername(String username);
/*通过用户id查找用户角色*/
List<Role> getUserRolesByUid(Integer id);
}
<?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="top.fanaozhe.springbootdemo.mapper.RuserMapper">
<select id="findUserByUsername" resultType="top.fanaozhe.springbootdemo.entity.Ruser">
SELECT * FROM ruser WHERE username = #{username}
</select>
<select id="getUserRolesByUid" resultType="top.fanaozhe.springbootdemo.entity.Role">
SELECT * FROM role r,ruser_role ur WHERE r.id=ur.rid and ur.uid =#{id}
</select>
</mapper>
@Mapper
public interface MenuMapper {
List<Menu> getAllMenus();
}
<?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="top.fanaozhe.springbootdemo.mapper.MenuMapper">
<resultMap id="BaseResultMap" type="top.fanaozhe.springbootdemo.entity.Menu">
<id property="id" column="id"/>
<result property="pattern" column="pattern"/>
<collection property="roles" ofType="top.fanaozhe.springbootdemo.entity.Role">
<id property="id" column="id"/>
<result property="name" column="rname"/>
<result property="nameZh" column="rnameZh"/>
</collection>
</resultMap>
<select id="getAllMenus" resultMap="BaseResultMap">
select m.*,r.id as rid,r.name as rname,r.nameZh as rnameZh from menu m
left join menu_role mr on m.id=mr.mid left join role r on mr.rid=r.id
</select>
</mapper>
五:调试
通过以上代码即可实现一个简单的动态权限控制,实现不同路径资源访问;