从零开始java安全权限框架篇(二):spring security实现权限

目录

一.spring security控制权限

二:后端验证

1.验证流程

2.配置

(1)配置自定义角色权限验证

(2)在config中声明改配置

(3).controller中配置权限

(4)注意点

(5)说明

三:权限控制按钮权限的显示

1.引入依赖

2.页面引入与控制

3.使用标签

4.修改登录方法


一.spring security控制权限

  security的核心功能就是控制权限。这里,我们通过以下几个方面来说明:

  (1)数据库动态配置权限

  (2)前端页面控制(控制按钮或者菜单是否显示)

  (3)后端验证(验证某一个请求是否合法)

 

二:后端验证

1.验证流程

2.配置

(1)配置自定义角色权限验证

  在security中,权限的验证是由PermissionEvaluator来实现的,这里我们需要自定义并集成

package com.config.Seurity.permission;

import java.io.Serializable;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import com.config.Seurity.util.UserUtil;
import com.moudle.system.model.Menu;
import com.moudle.system.service.MenuService;

/**
 * 
 * ClassName: 自定义URL拦截 
 * Function: 一句话描述功能. 
 * auth: monxz
 * date: 2019年8月29日 下午7:23:44 
 *
 *
 */
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator{
	
	@Autowired
	private MenuService  menuService;

	/**
	 * 我的url和权限全部放在菜单表中了
	 * TODO 注意这里,我们这里直接加载菜单中URL以及权限,是应为在MenuService中自动过滤掉了相应权限的路径了
	 * @see 
	 */
	@Override
	public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
		
		if(UserUtil.currentUser().getRole().getRoleRange() == 0) {
			return true;
		}
		
		List menuList=menuService.findList(new Menu());		
		for(Menu menu:menuList) {
//			if(targetDomainObject.equals(menu.getUrl())  && permission.equals(menu.getParams())) {
//				return true;
//			}
			if( permission.equals(menu.getParams())) {
				return true;
			}
		}
		
		return false;
	}

	@Override
	public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
			Object permission) {
		
		// TODO Auto-generated method stub
		return false;
	}

}

(2)在config中声明改配置

package com.config.Seurity.config;



import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import com.config.Seurity.hander.AjaxSuccessHander;
import com.config.Seurity.hander.AjaxfailHander;

import com.config.Seurity.permission.CustomPermissionEvaluator;
import com.config.Seurity.pwdEnder.MyPasswordEncoder;
import com.config.Seurity.service.LoginService;

import lombok.extern.slf4j.Slf4j;





/**
 * spring security的配置
 * ClassName: SecurityConfig 
 * Function: 一句话描述功能. 
 * auth: monxz
 * date: 2019年8月28日 上午10:04:50 
 *
 *
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j

public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
		@Autowired
		private LoginService loginService;
	
		@Autowired
		private DataSource dataSource;
		
		

		
	    // 注入自定义url和权限验证器	     
	    @Bean
	    public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
	        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
	        handler.setPermissionEvaluator(new CustomPermissionEvaluator());
	        return handler;
	    }
		
		
		//记住我功能的Token存储在数据库中
		@Bean
	    public PersistentTokenRepository persistentTokenRepository() {
	        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
	        // 配置数据源
	        jdbcTokenRepository.setDataSource(dataSource);
	        // 第一次启动的时候自动建表(可以不用这句话,自己手动建表,源码中有语句的)
//	         jdbcTokenRepository.setCreateTableOnStartup(true);
	        return jdbcTokenRepository;
	    }
		
		

		//登录执行的逻辑
		 @Autowired
		 public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
		      auth.userDetailsService(loginService).passwordEncoder(new MyPasswordEncoder());
		     
		  }

		 
		//配置信息
		 @Override
		    protected void configure(HttpSecurity http) throws Exception { 
			 	String[]  allowedUrl= {"/api/**"};
		       
			 	//配置访问权限
		        http.authorizeRequests()
		        		//允许匿名访问(如api)
		                .antMatchers(allowedUrl)
		                .permitAll()		                        
		                //其他地址的访问均需验证权限
		                .anyRequest()
		                .authenticated();
		        		
		        
		         //配置登录以及成功失败的处理方式
		         http.formLogin()		               
		                //指定登录页是"/view/login"   
		                .loginPage("/view/login").permitAll()    //
		                
		                
		                //ajax方式登录		               
		                .successHandler(new AjaxSuccessHander())
		                .failureHandler(new AjaxfailHander())
		                .loginProcessingUrl("/login")
		                .usernameParameter("username")  //ajax请求必须的
		                .passwordParameter("password");
		                
		                //form表单登录
//		                .defaultSuccessUrl("/view/index")       //登录成功后默认跳转到路径"
		               
		                
		         //注销 ,直接访问   ip:port/logout		
		         http .logout()
		                .logoutSuccessUrl("/view/login")          //退出登录后跳转到登录主界面"
		                .deleteCookies()						//有记住我功能,删除cookie
		                .permitAll();
		         	
		                
		         //记住我
		         http.rememberMe()
		          		.tokenRepository(persistentTokenRepository())
		          		.tokenValiditySeconds(60);
		         
		                //跨域以及其他的一些配置
		         http .csrf()
		         	  .disable()   // 关闭CSRF跨域
		              .headers()
		              .frameOptions()
		              .sameOrigin();  // 允许加载frame子菜单

		    }
		 
		 //静态资源
		 @Override
		  public void configure(WebSecurity web) throws Exception {
			 	String[] allowedRes= {"/static/**","/css/**","/js/**","/my/**","/img/**","/ajax/**","favicon.ico"};
			 			
		        // 设置拦截忽略文件夹,可以对静态资源放行
		        web.ignoring().antMatchers(allowedRes);
		   }

}

(3).controller中配置权限

这里我就举个例子

@PreAuthorize("hasPermission('/user/user/list','user:user:list')")
	@RequestMapping(value = {"/list"})
	public DataResult list(User user) {		
		
		startPage();		
		PageInfo page=new PageInfo(userService.findList( user));
		return DataResult.buildSuccess(page);
	}

(4)注意点

 在我配置用户管理没有权限时,前端/后台请求时这样的,很奇怪即使没有权限你也放回个没有数据吧。

看到后台的结果发现不允许请求是以异常的方式跑出来的。因此我去了项目统一异常处理中看了一下:

从零开始java安全权限框架篇(二):spring security实现权限_第1张图片

原来是我的ajax请求并没有返回前端分页所需的page信息。

(5)说明

  在上面我只是对ajax请求做了返回,但是页面的跳转并没有处理,我是这样考虑的,即使你请求到页面,但是页面的各种按钮权限你是没有的。

 

三:前端页面权限的显示

  当前一个用户只有查询用户的权限,但是没有修改用户的权限,这里我们在该用户前端页面就不应该有修改按钮的显示。

   html标签控制元素显示:

从零开始java安全权限框架篇(二):spring security实现权限_第2张图片

js控制元素显示

目录

一.spring security控制权限

二:后端验证

1.验证流程

2.配置

(1)配置自定义角色权限验证

(2)在config中声明改配置

(3).controller中配置权限

(4)注意点

(5)说明

三:前端页面权限的显示

1.引入依赖

2.页面引入与控制

3.使用标签

4.修改登录方法

5.在js中获取到权限


1.引入依赖

  该功能依赖于springsecurity.tld的相关jar包,在mavne文件中引入:

    

  注意这里springsecurity5最好和你的项目里的springescurity版本匹配,我这里通过mavne的版本可以看到

     从零开始java安全权限框架篇(二):spring security实现权限_第3张图片

2.页面引入与控制

3.使用标签

从零开始java安全权限框架篇(二):spring security实现权限_第4张图片

 注意,我后来测试了sec:authorize="hahsRole('')"有局限性,跟版本有关系,建议换成sec:authorize="hahsAuthory('   ')"

4.修改登录方法

  记过多次的测试,发现3步骤只能对于对于 角色 (Role)做控制,而不能针对具体请求具体的权限。这里的加载的是登录时验证的角色,因此修改登录方法,具体到某一个权限()

package com.config.Seurity.service;

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

import org.apache.tomcat.util.security.MD5Encoder;
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.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.config.Seurity.model.CurrentUser;
import com.moudle.system.dao.MenuDao;
import com.moudle.system.model.Menu;
import com.moudle.system.service.MenuService;
import com.moudle.user.model.Role;
import com.moudle.user.model.User;
import com.moudle.user.model.UserRole;
import com.moudle.user.service.RoleMenuService;
import com.moudle.user.service.RoleService;
import com.moudle.user.service.UserRoleService;
import com.moudle.user.service.UserService;

import commons.utils.Md5Utils;

/*
 * 自定义用户登录授权service
 */
@Service("userDetailsService")
public class LoginService implements  UserDetailsService{
	
	@Autowired
	private UserService  userService;
	

	@Autowired
	private MenuDao  menuDao;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
		List  userList = userService.login(username, null);		
		if (userList == null || userList.isEmpty()) {
			throw new UsernameNotFoundException("该用户不存在!");
		}		
		User  currentUser=userList.get(0);
		
		//添加权限
		List  permsList=new ArrayList();
		permsList =currentUser.getPerms()==null?
				new ArrayList():currentUser.getPerms();
		
		if(currentUser.getRole().getRoleRange() == 0) {
			//超级管理员
			List  menuList=menuDao.findList(new Menu())==null 
					? new ArrayList():menuDao.findList(new Menu());
					
			for(Menu menu:menuList) {
				permsList.add(menu.getPerms());
			}		
		
		}
		
		//授权
		Collection authorities = new ArrayList<>();		
		permsList.forEach(o->{
			if(o!=null && o.trim().length() !=0) {
				authorities.add(new SimpleGrantedAuthority(o));
			}		
		});			
//		return new CurrentUser(currentUser.getFkUserId(),currentUser.getRole(),username, currentUser.getPassword(), authorities);
		return new CurrentUser(currentUser, authorities);
	}

}

5.在js中获取到权限

  在上面的例子中,我们看到了的是在Html的标签使用Security的,但是如果我们在js中,就比如下面的我使用bootstrap来在js中构建Table,但是当前用户并没有 编辑 权限怎么办啊?我翻阅了很多资料,都没有看到相关的处理从零开始java安全权限框架篇(二):spring security实现权限_第5张图片

  这里我们使用了thyleaf模板渲染页面,也可以使用它去验证权限。

从零开始java安全权限框架篇(二):spring security实现权限_第6张图片

我们看一下做了几个事情?

(1)将当前的js域声明可以使用thymeleaf:

(2)调用后台方法   

           这里首先去后台声明@Service(“menu”)中的findByPerms()方法

 (3)既然第(2)调用了后台方法,那么后台这个方法怎么定义呢?

         从零开始java安全权限框架篇(二):spring security实现权限_第7张图片

  其实就是后台查询权限的service的实现层(我的权限在菜单中),我在@Service后加了一个("menu")声明罢了,然后在这个类中声明一个前台刚才说明的方法:

                       从零开始java安全权限框架篇(二):spring security实现权限_第8张图片

 其实,看到这里就是用Thymeleaf去查询方法,建议这个方法中的数据直接放入缓存,而不是数据库中,以免增加执行时间。

你可能感兴趣的:(从零开始)