【权限管理】springboot集成security

摘自:

  https://www.cnblogs.com/hhhshct/p/9726378.html

  https://blog.csdn.net/weixin_42849689/article/details/89957823

  https://blog.csdn.net/zhaoxichen_10/article/details/88713799

  http://www.imooc.com/article/287214

一、Spring Security简介

  Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。它是一个轻量级的安全框架,它确保基于Spring的应用程序提供身份验证和授权支持。它与Spring MVC有很好地集成,并配备了流行的安全算法实现捆绑在一起。安全主要包括两个操作“认证”与“验证”(有时候也会叫做权限控制)。“认证”是为用户建立一个其声明的角色的过程,这个角色可以一个用户、一个设备或者一个系统。“验证”指的是一个用户在你的应用中能够执行某个操作。在到达授权判断之前,角色已经在身份认证过程中建立了。

  用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。
  访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。

二、Spring Security的执行过程

三、Spring Security代码实现

Spring Security的核心配置类是 WebSecurityConfig,抽象类
这是权限管理启动的入口,这里我们自定义一个实现类去它。然后编写我们需要处理的控制逻辑。
下面是代码,里面写的注释也比较详细。在里面还依赖了几个自定义的类,都是必须配置的。分别是
userService,
myFilterInvocationSecurityMetadataSource,
myAccessDecisionManager,

authenticationAccessDeniedHandler

3.1 WebSecurityConfig

package com.example.demo.config;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import com.example.demo.service.UserService;
/**
 * spring-security权限管理的核心配置
 * @author wjqhuaxia
 *
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) //全局
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;//实现了UserDetailsService接口
    @Autowired
    private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;//权限过滤器(当前url所需要的访问权限)
    @Autowired
    private MyAccessDecisionManager myAccessDecisionManager;//权限决策器
    @Autowired
    private AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;//自定义错误(403)返回数据

    /**
     * 自定义的加密算法
     * @return
     */
    @Bean
    public PasswordEncoder myPasswordEncoder() {
    	return new MyPasswordEncoder();
    }
    /**
     *  配置userDetails的数据源,密码加密格式
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(myPasswordEncoder());
    }
    /**
     * 配置放行的资源
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
           .antMatchers("/index.html", "/static/**","/loginPage","/register")
           // 给 swagger 放行;不需要权限能访问的资源
           .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/images/**", "/webjars/**", "/v2/api-docs", "/configuration/ui", "/configuration/security");
    }

    /**
     * 这段配置,我认为就是配置Security的认证策略, 每个模块配置使用and结尾。
		authorizeRequests()配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
		formLogin()对应表单认证相关的配置
		logout()对应了注销相关的配置
		httpBasic()可以配置basic登录
     */
    /**
     * HttpSecurity包含了原数据(主要是url)
     * 1.通过withObjectPostProcessor将MyFilterInvocationSecurityMetadataSource和MyAccessDecisionManager注入进来
     * 2.此url先被MyFilterInvocationSecurityMetadataSource处理,然后 丢给 MyAccessDecisionManager处理
     * 3.如果不匹配,返回 MyAccessDeniedHandler
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    		// authorizeRequests()配置路径拦截,表明路径访问所对应的权限,角色,认证信息
        	http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);
                        o.setAccessDecisionManager(myAccessDecisionManager);
                        return o;
                    }
                })
                .and()
            // formLogin()对应表单认证相关的配置
            .formLogin()
            	.loginPage("/loginPage")
            	.loginProcessingUrl("/login")
            	.usernameParameter("username")
            	.passwordParameter("password")
            	.permitAll()
            .failureHandler(new AuthenticationFailureHandler() {
	            @Override
	            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
	                httpServletResponse.setContentType("application/json;charset=utf-8");
	                PrintWriter out = httpServletResponse.getWriter();
	                StringBuffer sb = new StringBuffer();
	                sb.append("{\"status\":\"error\",\"msg\":\"");
	                if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
	                    sb.append("用户名或密码输入错误,登录失败!");
	                } else {
	                    sb.append("登录失败!");
	                }
	                sb.append("\"}");
	                out.write(sb.toString());
	                out.flush();
	                out.close();
	            }
            }).successHandler(new AuthenticationSuccessHandler() {
            @Override
	            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
	                httpServletResponse.setContentType("application/json;charset=utf-8");
	                PrintWriter out = httpServletResponse.getWriter();
	                String s = "{\"status\":\"success\",\"msg\":\"登陆成功\"}";
	                out.write(s);
	                out.flush();
	                out.close();
	            }
            }).and()
            // logout()对应了注销相关的配置
            .logout()
            	.permitAll()
            	.and()
            	.csrf()
            	.disable()
        	.exceptionHandling()
        		.accessDeniedHandler(authenticationAccessDeniedHandler);
    }
}

3.2 UserService

UserServiceImpl实现了UserDetailsService接口中的loadUserByUsername方法,方法执行成功后返回UserDetails对象,为构建Authentication对象提供必须的信息。UserDetails中包含了用户名,密码,角色等信息。

package com.example.demo.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.dao.PermissionMapper;
import com.example.demo.dao.RoleMapper;
import com.example.demo.dao.UserMapper;
import com.example.demo.model.Permission;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
/**
 * 实现了UserDetailsService接口中的loadUserByUsername方法
 * 执行登录,构建Authentication对象必须的信息,
 * 如果用户不存在,则抛出UsernameNotFoundException异常
 * @author wjqhuaxia
 *
 */
@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private PermissionMapper permissionMapper;
	@Autowired
	private RoleMapper roleMapper;
	@Autowired
	private UserMapper userMapper;
	@Autowired
	private PasswordEncoder passwordEncoder;
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userMapper.selectByUsername(username);
		if (user != null) {
            List<Permission> permissions = permissionMapper.findByUserId(user.getId());
            List<GrantedAuthority> grantedAuthorities = new ArrayList <>();
            for (Permission permission : permissions) {
                if (permission != null && permission.getPermissionname()!=null) {

                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getPermissionname());
                grantedAuthorities.add(grantedAuthority);
                }
            }
            return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
        } else {
            throw new UsernameNotFoundException("username: " + username + " do not exist!");
        }
	}

	@Transactional
	@Override
	public void userRegister(String username, String password) {
		User user  = new User();
		user.setUsername(passwordEncoder.encode(username));
		user.setPassword(password);
		userMapper.insert(user);
		User rtnUser =userMapper.selectByUsername(username);
		//注册成功默认给用户的角色是user
		roleMapper.insertUserRole(rtnUser.getId(), 2);
	}

}

3.3 MyFilterInvocationSecurityMetadataSource

自定义权限过滤器,继承了 SecurityMetadataSource(权限资源接口),过滤所有请求,核查这个请求需要的访问权限;主要实现Collection<ConfigAttribute> getAttributes(Object o)方法,此方法中可编写用户逻辑,根据用户预先设定的用户权限列表,返回访问此url需要的权限列表。

package com.example.demo.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.example.demo.dao.PermissionMapper;
import com.example.demo.model.Permission;
/**
 * 自定义权限过滤器
 * FilterInvocationSecurityMetadataSource(权限资源过滤器接口)继承了 SecurityMetadataSource(权限资源接口)
 * Spring Security是通过SecurityMetadataSource来加载访问时所需要的具体权限;Metadata是元数据的意思。
 * 自定义权限资源过滤器,实现动态的权限验证
 * 它的主要责任就是当访问一个url时,返回这个url所需要的访问权限
 * @author wjqhuaxia
 *
 */
@Service
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	private static final Logger log = LoggerFactory.getLogger(MyFilterInvocationSecurityMetadataSource.class);

	@Autowired
	private PermissionMapper permissionMapper;

	private HashMap<String, Collection<ConfigAttribute>> map = null;

	/**
	 * 加载权限表中所有权限
	 */
	public void loadResourceDefine() {
		map = new HashMap<String, Collection<ConfigAttribute>>();

		List<Permission> permissions = permissionMapper.findAll();
		for (Permission permission : permissions) {
			if(StringUtils.isEmpty(permission.getPermissionname())){
				continue;
			}
			if(StringUtils.isEmpty(permission.getUrl())){
				continue;
			}
			ConfigAttribute cfg = new SecurityConfig(permission.getPermissionname());
			List<ConfigAttribute> list = new ArrayList<>();
			list.add(cfg);
			// TODO:如果一个url对应多个权限,这里有问题
			map.put(permission.getUrl(), list);
		}

	}

	/**
	 * 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法, 用来判定用户
	 * 是否有此权限。如果不在权限表中则放行。
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		if (map == null) {
			loadResourceDefine();
		}
		// object 中包含用户请求的request的信息
		HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
		for (Entry<String, Collection<ConfigAttribute>> entry : map.entrySet()) {
			String url = entry.getKey();
			if (new AntPathRequestMatcher(url).matches(request)) {
				return map.get(url);
			}
		}
		/**
         * @Author: Galen
         * @Description: 如果本方法返回null的话,意味着当前这个请求不需要任何角色就能访问
         * 此处做逻辑控制,如果没有匹配上的,返回一个默认具体权限,防止漏缺资源配置
         **/
        log.info("当前访问路径是{},这个url所需要的访问权限是{}", request.getRequestURL(), "ROLE_LOGIN");
        return SecurityConfig.createList("ROLE_LOGIN");
	}
	/**
	 * 此处方法如果做了实现,返回了定义的权限资源列表,
     * Spring Security会在启动时校验每个ConfigAttribute是否配置正确,
     * 如果不需要校验,这里实现方法,方法体直接返回null即可
	 */
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}
	/**
	 * 方法返回类对象是否支持校验,
     * web项目一般使用FilterInvocation来判断,或者直接返回true
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		return true;
	}

}

3.4 AuthenticationAccessDeniedHandler

自定义权限决策管理器,需要实现AccessDecisionManager 的 void decide(Authentication auth, Object object, Collection<ConfigAttribute> cas) 方法,在上面的过滤器中,我们已经得到了访问此url需要的权限;那么,decide方法,先查询此用户当前拥有的权限,然后与上面过滤器核查出来的权限列表作对比,以此判断此用户是否具有这个访问权限,决定去留!所以顾名思义为权限决策器。

package com.example.demo.config;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
 * 拒签(403响应)处理器
 * Denied是拒签的意思
 * @author wjqhuaxia
 *
 */
@Component
public class AuthenticationAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse resp, AccessDeniedException e) throws IOException, ServletException {
        resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
        resp.setContentType("application/json;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员!\"}");
        out.flush();
        out.close();
    }
}

原文地址:https://www.cnblogs.com/wjqhuaxia/p/12078671.html

时间: 2024-10-18 21:58:33

【权限管理】springboot集成security的相关文章

Spring安全权限管理(Spring Security)

1.spring Security简要介绍 Spring Security以前叫做acegi,是后来才成为Spring的一个子项目,也是目前最为流行的一个安全权限管理框架,它与Spring紧密结合在一起. Spring Security关注的重点是在企业应用安全层为您提供服务,你将发现业务问题领域存在着各式各样的需求.银行系统跟电子商务应用就有很大的不同.电子商务系统与企业销售自动化工具又有很大不同.这些客户化需求让应用安全显得有趣,富有挑战性而且物有所值.Spring Security为基于J

SpringBoot集成gRPC微服务工程搭建实践

前言本文将使用Maven.gRPC.Protocol buffers.Docker.Envoy等工具构建一个简单微服务工程,笔者所使用的示例工程是以前写的一个Java后端工程,因为最近都在学习微服务相关的知识,所以利用起来慢慢的把这个工程做成微服务化应用.在实践过程踩过很多坑,主要是经验不足对微服务还是停留在萌新阶段,通过本文记录创建微服务工程碰到一些问题,此次实践主要是解决以下问题: 如何解决.统一服务工程依赖管理SpringBoot集成gRPC管理Protocol buffers文件使用En

SpringBoot 集成 Shiro:使用Shiro的权限管理(六)

上一章使用了Shiro的角色管理,现在加入粒度更小的权限管理,即根据用户角色分配的权限来判断用户能否访问页面 准备实体类和修改数据源 @Getter @Setter public class Role implements Serializable { private String name; private Set<Permission> permissions; public Role(String name) { this.name = name; this.permissions =

SpringBoot集成Spring Security(5)——权限控制

在第一篇中,我们说过,用户<–>角色<–>权限三层中,暂时不考虑权限,在这一篇,是时候把它完成了. 为了方便演示,这里的权限只是对角色赋予权限,也就是说同一个角色的用户,权限是一样的.当然了,你也可以精细化到为每一个用户设置权限,但是这不在本篇的探讨范围,有兴趣可以自己实验,原理都是一样的. 源码地址:https://github.com/jitwxs/blog_sample 文章目录 一.数据准备 1.1 创建 sys_permission 表 1.2 创建 POJO.Mappe

SpringBoot(十四):springboot整合shiro-登录认证和权限管理

原文出处: 纯洁的微笑 这篇文章我们来学习如何使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉及到这方面的需求.在Java领域一般有Spring Security.Apache Shiro等安全框架,但是由于Spring Security过于庞大和复杂,大多数公司会选择Apache Shiro来使用,这篇文章会先介绍一下Apache Shiro,在结合Spring Boot给出使用案例. Apache Shiro What is Ap

Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 PageHelper 进行分页显示.按照前面的博客,已经可以搭建一个简单的 Spring Boot 系统,本篇博客继续对此系统进行改造,主要集成了 Shiro 权限认证框架,关于 Shiro 部分,在本人之前的博客(认证与Shiro安全框架)有介绍到,这里就不做累赘的介绍. 此系列的博客为实践部分

SpringBoot集成Shiro 实现动态加载权限

一.前言 本文小编将基于 SpringBoot 集成 Shiro 实现动态uri权限,由前端vue在页面配置uri,Java后端动态刷新权限,不用重启项目,以及在页面分配给用户 角色 . 按钮 .uri 权限后,后端动态分配权限,用户无需在页面重新登录才能获取最新权限,一切权限动态加载,灵活配置 基本环境 spring-boot 2.1.7 mybatis-plus 2.1.0 mysql 5.7.24 redis 5.0.5 温馨小提示:案例demo源码附文章末尾,有需要的小伙伴们可参考哦 ~

使用Spring Security实现权限管理

使用Spring Security实现权限管理 1.技术目标 了解并创建Security框架所需数据表 为项目添加Spring Security框架 掌握Security框架配置 应用Security框架为项目的CRUD操作绑定权限 2.权限管理需求描述 为系统中的每个操作定义权限,如定义4个权限: 1)超级权限,可以使用所有操作 2)添加影片权限 3)修改影片权限 4)删除影片权限 为系统设置管理员帐号.密码 为系统创建权限组,每个权限组可以配置多个操作权限,如创建2个权限组: 1)"Admi

Springmvc集成Shiro实现权限管理

Shiro是一个安全框架,他可以集成其他开发开发框架 如:Springmvc,实现用户身份认证.权限管理等等功能,shiro详细的介绍也就不讲了,这里给出一些关键的知识点吧: 知识点: shiro中默认的过滤器 过滤器名称 过滤器类 描述 anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名过滤器 authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 如果继续