springboot+springsecurity基于用户表-角色表-权限表的权限控制(三)

用户实体类

参考:springboot+springsecurity基于角色的权限验证(二)

配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled =true) // 启用授权注解
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {	
		
    @Autowired
    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
    
	/** 获取数据库中信息存到User对象中 */
	@Bean
    public UserDetailsService userService(){ //注册userService 的bean
        return new UserServiceImpl();
    }
	/**加密密码*/
	@Bean
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	/**MD5加密密码*/
	@Bean
	PasswordEncoder md5PasswordEncoder() {
		return new MD5PasswordEncoder();
	}
	
	/** 放行静态资源 */
	@Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/images/**");
        web.ignoring().antMatchers("/login/**");
        //解决服务注册url被拦截的问题
        web.ignoring().antMatchers("/resources/**");
    }
	
	@Override
    protected void configure(HttpSecurity http) throws Exception {
		// 登录
		http.formLogin().loginPage("/toLogin").loginProcessingUrl("/doLogin")
		    .defaultSuccessUrl("/index")
		    .failureUrl("/toLogin?error=true");  
		//解决非thymeleaf的form表单提交被拦截问题
		http.csrf().disable();
		
		http
			.authorizeRequests()
			.antMatchers("/toLogin")
			.permitAll()
            .anyRequest()
            .authenticated()				
        ;
					
        //session管理
        //session失效后跳转到登录页面  
        http.sessionManagement().invalidSessionUrl("/toLogin");

        //单用户登录,如果有一个登录了,同一个用户在其他地方登录将前一个剔除下线
        //http.sessionManagement().maximumSessions(1).expiredSessionStrategy(expiredSessionStrategy());
        
		//单用户登录,如果有一个登录了,同一个用户在其他地方不能登录
	    http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
	    //退出删除cookie
	    http.logout()
	        .permitAll()
	    	.logoutUrl("/logout")  //执行注销的url
	    	.invalidateHttpSession(true) // 指定是否在注销时让httpSession无效
	    	.deleteCookies("JESSIONID")  // 清除cookie
	    	.logoutSuccessUrl("/toLogin"); // 注销成功后跳转的url
	    super.configure(http);
	
	    //解决中文乱码问题
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        //http.addFilterBefore(filter,CsrfFilter.class);

        http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
		
	}
	
	/**
	 * 认证器
	 */
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// 自定义加密
		auth.userDetailsService(userService()).passwordEncoder(md5PasswordEncoder());		
	}

	
}

创建决策类 MyAccessDecisionManager

/**
 * 决策管理器(授权)
 *
 * @ClassName: MyAccessDecisionManager
 * @description 
 * @author Jayden
 * @createDate 2019年4月8日-下午2:02:32
 */
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {

	/**
	 * decide 方法是判定是否拥有权限的决策方法,
	 * authentication 是释UserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
	 * object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
	 * 
	 * configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,
	 * 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
	 * 
	 */
	@Override
	public void decide(Authentication authentication, Object obj, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
		
		if(null== configAttributes || configAttributes.size() <= 0) {
            return;
        }
        ConfigAttribute c;
        String needRole;
        for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
            c = iter.next();
            needRole = c.getAttribute();
            //authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
            for(GrantedAuthority ga : authentication.getAuthorities()) {
                if(needRole.trim().equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        
        throw new AccessDeniedException("no right");
	}
	
	@Override
	public boolean supports(ConfigAttribute configattribute) {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean supports(Class<?> class1) {
		// TODO Auto-generated method stub
		return true;
	}

}

创建类 MyFilterSecurityInterceptor

@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

	@Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
	
	@Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }
	
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);

	}

	public void invoke(FilterInvocation fi) throws IOException, ServletException {
		// fi里面有一个被拦截的url
		// 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object
		// object)这个方法获取fi对应的所有权限
		// 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
		InterceptorStatusToken token = super.beforeInvocation(fi);
		try {
			// 执行下一个拦截器
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		} finally {
			super.afterInvocation(token, null);
		}
	}
	
	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
	}

	@Override
	public Class<?> getSecureObjectClass() {
		// TODO Auto-generated method stub
		return FilterInvocation.class;
	}

	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		// TODO Auto-generated method stub
		return this.securityMetadataSource;
	}	
	
}

创建类 MyInvocationSecurityMetadataSourceService

@Service
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

	@Autowired
    private T_permissionMapper permissionMapper;

    private HashMap<String, Collection<ConfigAttribute>> map =null;
	
    /**
     * 加载权限表中所有权限
     */
    public void loadResourceDefine(){
        map = new HashMap<>();
        Collection<ConfigAttribute> array;
        ConfigAttribute cfg;
        // 查询到权限表所有信息
        List<T_permission> permissions = permissionMapper.findAll();
        
        for(T_permission permission : permissions) {
            array = new ArrayList<>();
            cfg = new SecurityConfig(permission.getName());
            /**
             *此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。
             *此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
             */
            array.add(cfg);
            //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
            map.put(permission.getUrl(), array);
        }

    }
    
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,
	 * 用来判定用户是否有此权限。如果不在权限表中则放行。
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		if(map ==null) loadResourceDefine();
        //object 中包含用户请求的request 信息
        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        AntPathRequestMatcher matcher;
        String resUrl;
        for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            resUrl = iter.next();
            matcher = new AntPathRequestMatcher(resUrl);
            if(matcher.matches(request)) {
                return map.get(resUrl);
            }
        }
        return null;
	}

	@Override
	public boolean supports(Class<?> class1) {
		// TODO Auto-generated method stub
		return true;
	}

}

权限 PermissionMapper.xml

用户表,角色表,权限表,用户角色中间表,角色权限中间表

<?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.jayden.mapper.T_permissionMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.jayden.entity.T_permission">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="description" property="description" />
        <result column="url" property="url" />
        <result column="pid" property="pid" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, description, url, pid
    </sql>

    <!-- 通过userid查询权限信息 -->
    <select id="selectPermsByUserId" resultMap="BaseResultMap">
        select p.* 
        from t_user u       
        LEFT JOIN t_user_role tur on u.id = tur.user_id
        LEFT JOIN t_role r on tur.role_id = r.id
        LEFT JOIN t_role_permission trp on trp.role_id = r.id
        LEFT JOIN t_permission p on p.id = trp.permission_id
        where u.id=#{userId}
    </select>
    
    <!-- 查询所有权限 -->
    <select id="findAll" resultMap="BaseResultMap">
        select * from t_permission
    </select>

UserDetailsService 实现类

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, T_user> implements UserService, UserDetailsService  {
    // 用户
	@Autowired
	private UserMapper userMapper;
	// 权限
	@Autowired
	private T_permissionMapper permissionMapper;
	// 加密
	@Autowired
	private MD5PasswordEncoder md5PasswordEncoder;		
	
	/**
	 * 重写springSecurity类方法
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 通过用户名称获取对象
		T_user user = userMapper.findByUserName(username);
		
		if (user != null) {
		    // 因为数据库中密码是明文的,所以在这里加密了(仅测试使用:123为盐)
			user.setPassword(md5PasswordEncoder.encode(user.getPassword()+"123"));	
			// 通过用户id获取权限信息	
			List<T_permission> permissions = permissionMapper.selectPermsByUserId(user.getId());
	        List<GrantedAuthority> grantedAuthorities = new ArrayList <>();
	        
	        for (T_permission permission : permissions) {
	            if (permission != null && permission.getName() !=null) {
	                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());
	                //此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象
	                grantedAuthorities.add(grantedAuthority);
	            }
	        }
	        
	        return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
		} else {
			System.out.println("该用户不存在");
			throw new UsernameNotFoundException("用户: " + username + " 不存在!");
		}
				
	}

	
}
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
项目描述 说明: spring security 全注解式的权限管理 动态配置权限角色和资源,权限控制到按钮粒度 采用token进行权限校验,禁用session,未登录返回401,权限不足返回403 采用redis存储token及权限信息 内置功能: 用户管理:用户查询、添加用户、修改用户、给用户分配角色 菜单管理:菜单列、添加菜单、修改菜单、删除菜单、权限配置、菜单图标设置、菜单排序 角色管理:角色查询、添加角色、修改角色、删除角色 代码生成:根据名生成bean、controller、dao、Mapper.xml、列页、搜索、分页、新增页、修改页 job集群:创建job、取消job、查询job、下拉搜索spring bean 数据源监控:druid 接口swagger文档 日志查询 邮件管理:发送邮件、搜索邮件 文件管理:上传文件、文件列、文件删除 公告管理:公告未读提醒、发布公告、查询公告、公告阅读人列 excel下载:自定义sql导出excel、也可在页面展示sql结果数据 字典管理:一些常量字典的维护 个人信息修改 修改密码 头像修改 其他说明: 日志模块 sl4j日志分包:将sql日志、业务日志、异常日志进行了分离,更方便定位问题 日志:使用aop拦截实现 权限控制:基于token方式,禁用session 对各种不同异常进行了全局统一处理 使用lombok简化java代码,让源码更简洁,可读性高 mybatis未进行二次封装,原滋原味,简单sql采用注解,复杂sql采用Mapper.xml配置 使用了layui的弹出层、菜单、文件上传、富文本编辑、日历、选项卡、数据格等 单数据采用bootstrapValidator校验,简单快捷方便 运行环境 jdk8+mysql+redis+IntelliJ IDEA+maven 项目技术(必填) Springboot+Mybatis+ SpringMvc+springsecrity+Redis+bootstrap+jquery 数据库文件 压缩包内 jar包文件 maven搭建

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值