Shiro框架基础使用及一些小拓展

配置Web环境中出现异常时如何解决不同异常跳转到不同页面方法

@Configuration
public class WebExceptionHandlerConfig {
	@Bean
	public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
		SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
		Properties props = new Properties();
		//让特定的异常类型和特定的页面对应起来,第二个参数是页面的名称
		props.put("org.apache.shiro.authz.UnauthorizedException", "/unauthor");
		resolver.setExceptionMappings(props);
		return resolver;
	}
}

对外共享文件夹

在.yml 配置文件设置上传文件 抵达的文件夹路径

bootdo:
	uploadPath: F:/Downdload/

先定义上传路径的类

@Component
@ConfigurationProperties(prefix="bootdo")
public class BootdoConfig {
	//上传路径
	private String uploadPath;

	public String getUploadPath() {
		return uploadPath;
	}

	public void setUploadPath(String uploadPath) {
		this.uploadPath = uploadPath;
	}
}

在config目录下 新建用于映射类 (继承WebMvcConfigurerAdapter)

@Component
class WebConfigurer extends WebMvcConfigurerAdapter {
		@Autowired
		BootdoConfig bootdoConfig;
		
	public void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler("/files/**")
		.addResourceLocations("file:///"+bootdoConfig.getUploadPath());
}		

}

MD5加密


public class MD5Util {
	/**
	 * 验证MD5密码是否正确
	 * 
	 * @param userPwd 用户提供的密码
	 * @param dbPwd   数据库的密码
	 * @param salt    加密的盐(可以是用户名)
	 * @return
	 */
	public static boolean validatePwd(String userPwd, String dbPwd, String salt) {
		boolean result = false;
		String toMd5 = userPwd + salt;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] digest = md.digest(toMd5.getBytes("UTF-8"));
			String encodePwd = Base64.getEncoder().encodeToString(digest);//这是根据用户输入和盐转化后得到的散列密码
			return encodePwd.equals(dbPwd);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return result;

	}

	//手动验证
	public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		String toMd5 ="123456user1";
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] digest = md.digest(toMd5.getBytes("UTF-8"));
		String encodePwd = Base64.getEncoder().encodeToString(digest);
		System.out.println(encodePwd);
	}
}

ShiroUtil

获取当前环境下登陆的用户对象 此类放进utils包里

public static UserDO getCurrentUser(){
		UserDO user =  (UserDO)SecurityUtils.getSubject().getPrincipal();
		return user;
}

shiro的鉴权与授权(userrealm)

在这里插入图片描述
在userrealm包新建MyUserRealm类继承 AuthorizingRealm 会继承2个方法

  • 告诉shiro哪个subject有哪些访问权限(doGetAuthorizationInfo )
  • 实际的登陆验证(doGetAuthenticationInfo)

public class MyUserRealm extends AuthorizingRealm{

	private SysUserService userService;
	
	public void setUserService(SysUserService userService) {
		this.userService = userService;
	}
	/**
	 * 告诉shiro哪个subject有哪些访问权限
	 */
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
	//查找用户
	SysUserDO user = (SysUserDO) SecurityUtils.getSubject().getPrincipal();
	List<SysMenuDO> list = userService.findMenusByUser(user.getUsername());
	Set<String> permissions = new HashSet<>();//Shiro的权限表示方式1,即使用一个字符串表示


	for(SysMenuDO m:list) {
		if(m.getPerms()!=null && !(m.getPerms().equals(""))) {
		permissions.add(m.getPerms());
	}
	}
	System.out.println("用户: "+user.getUsername()+" 的权限有:"+permissions);
	SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
	authorInfo.setStringPermissions(permissions);
	return authorInfo;
	}
	
	/**
	 * 实际的登陆验证
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		
		System.out.println("在userRealm中得到了spring的支持下,获取了userService: "+userService);
		String username = (String) token.getPrincipal();
		String password = new String((char[])token.getCredentials());
		SysUserDO  user = userService.findByUsername(username);
		if(user==null) {
			throw new AuthenticationException("用户错误");
		}
		//启用md5方式判断
		if(MD5Util.validatePwd(password, user.getPassword(),username)){
			return new SimpleAuthenticationInfo(user,password,getName());
		}else {
			throw new AuthenticationException("登陆失败");
		}
		
		
		
		

	}

}

在config包创建ShiroConfig类 (Shiro的权限基本控制和基础初始化配置)


@Configuration
public class ShiroConfig {
/**
 * 实现shiro权限过滤
 */
	@Bean("shiroFilter")//bean注解告诉springboot这个方法会返回一个标准的bean
	public ShiroFilterFactoryBean getFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
		bean.setSecurityManager(securityManager);
		bean.setUnauthorizedUrl("/sys/toLogin");
		bean.setLoginUrl("/sys/toLogin");//设定登陆地址,鉴权错误或者没有授权访问时自动跳转到该请求
		//权限的全局设置
		Map<String, String> auths = new HashMap<>();
		auths.put("/sys/toLogin","anon");//如果一个地址不需要权限访问,则设置为anno
		auths.put("/sys/doLogin","anon");
		auths.put("/project/sysRole/**","authc");//需要授权
		auths.put("/**","authc");//其他必须授权
		//静态资源的路径要能直接访问
		auths.put("/css/**","anon");
		auths.put("/fonts/**","anon");
		auths.put("/js/**","anon");
		bean.setFilterChainDefinitionMap(auths);
		return bean;
	}
	
	@Bean
	public SecurityManager securityManager(MyUserRealm myUserRealm) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myUserRealm);
		return securityManager;
	}
	
	@Bean
	public MyUserRealm mUserRealm(SysUserService userService) {
		MyUserRealm userRealm = new MyUserRealm();
		//进行一系列的操作比如注入属性
		userRealm.setUserService(userService);
		
		return userRealm;
	}
	/**
	 * 让shiro参与到spring的bean的管理中来
	 * @return
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}
	/**
	 * 扫描所有的bean寻找通知并将通知器所切入bean中
	 * @return
	 */
	@Bean
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
		creator.setProxyTargetClass(true);
		return  creator;
	}
	
	@Bean
	public AuthorizationAttributeSourceAdvisor attributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}
}

登陆的控制器

@RequestMapping("doLogin")
@ResponseBody
public Map<String,Object> doLogin(String username,String password,String vcode,HttpServletRequest req ){
	Map<String,Object> result = new HashMap<>();
	result.put("code","-1");
	result.put("msg","未知错误");

	//获取Subject对象 SecurityUtils是Shiro提供的类(自带)
	Subject subject = SecurityUtils.getSubject();
	//封装用户名和密码 UsernamePasswordToken也是Shiro提供的
	UsernamePasswordToken token = new UsernamePasswordToken(username,password.toCharArray());
	//将封装好的验证信息传递给subject去验证 实际上就是调用了userrealm里的鉴权
	try{
		subject.login(token);
		//获取权限并保存到session中
		UserDO user = ShiroUtil.getCurrentUser();
		LinkedHashMap<MenuDo,List<MenuDO>> map = menuService.listAllMenusByUserId(user.getUserId);
		req.getSession().setAttribute("menus",map);//将数据传入session中
	if(subject.isAuthenticated()){
		//通过验证
		result.put("code","0");
		result.put("msg","success");
	}catch(AuthenticationException ex){ //userrealm里的鉴权可能会抛出异常信息
		result.put("code","1");
		result.put("msg",ex.getMessage());
	}
	return result;
}
	
}

登陆的页面

用户名:<input type="text" name="username" id="username"><br/>
密码:<input type="password" name="password" id="password"><br/>
<input type="button" value="登陆" onclick="doLogin()"> 

<script type="text/javascript">

function doLogin(){
	var user = $("#username").val();
	var pass =$("#password").val();
	
	$.ajax({
		url:"/sys/doLogin",
		type:"post",
		data:{username:user,password:pass},
		success:function(data){
			if(data.code==0){
				alert('登陆成功')
			}else{
				alert('登陆失败'+data.msg);
			}
		}
	});
}

</script>

在MenuService业务类创建listAllMenusByUserId: 按照页面组件特点生成所有对应用户菜单集合

  • 通过用户id 查找出对应的权限集合 父级菜单与子级菜单
/**
	 * 按照也没组件的特点生成所有对应用户菜单的集合
	 */
	 @Autowired
	private SysUserDao sysUserDao;
	
	@Autowired
	private SysMenuDao menuDao;

	@Autowired
	private SysRoleMenuDao roleMenuDao;
	
	@Autowired
	private SysUserRoleDao userRoleDao;
	
	@Override
	public LinkedHashMap<SysMenuDO, List<SysMenuDO>> listAllMenusByUserId(long userId) {
		Map<String, Object> p = new HashMap<String, Object>();
		p.put("userId", userId);
		List<SysMenuDO> allMenus = new ArrayList<SysMenuDO>();
		// 先查询用户角色
		List<SysUserRoleDO> roles = userRoleDao.list(p);
		p.clear();
		for (SysUserRoleDO ur : roles) {
			p.put("roleId", ur.getRoleId());
			// 查询每一个role对应的权限
			List<SysRoleMenuDO> listRm = roleMenuDao.list(p);
			p.clear();
			for (SysRoleMenuDO rm : listRm) {
				SysMenuDO sysMenuDO = menuDao.get(rm.getMenuId());
				if (sysMenuDO != null) {
					allMenus.add(sysMenuDO);
				}
			}
		}
		// 整理结构:按照parentId, order 排序
		allMenus.sort(new Comparator<SysMenuDO>() {

			@Override
			public int compare(SysMenuDO o1, SysMenuDO o2) {
				if (o1 == null) {
					return -1;
				}
				if (o2 == null) {
					return 1;
				}
				if (o1.getParentId().intValue() != o2.getParentId()) {
					if (o1.getParentId() == null) {
						return -1;
					}
					if (o2.getParentId() == null) {
						return 1;
					}
					return (int) (o1.getParentId() - o2.getParentId());
				}
				if (o1.getOrderNum().intValue() != o2.getOrderNum()) {
					if (o1.getOrderNum() == null) {
						return -1;
					}
					if (o2.getOrderNum() == null) {
						return 1;
					}
					return o1.getOrderNum() - o2.getOrderNum();
				} else {
					return (int) (o1.getMenuId() - o2.getMenuId());
				}
			}
		});

		// 整理菜单到总结构中
		LinkedHashMap<SysMenuDO, List<SysMenuDO>> allMenuInOrder = new LinkedHashMap<>();
		Map<Long, SysMenuDO> allParents = new HashMap<>();
		for (SysMenuDO m : allMenus) {
			if (m == null) {
				continue;
			}
			if (m.getParentId() == null || m.getParentId() == 0) {
				allMenuInOrder.put(m, new ArrayList<SysMenuDO>());
				allParents.put(m.getMenuId(), m);
			} else {
				Long parentId = m.getParentId();
				SysMenuDO sysMenuDO = allParents.get(parentId);
				List<SysMenuDO> list = allMenuInOrder.get(sysMenuDO);
				if(list==null) {
					continue;//说明该用户有某个子菜单但是没有父菜单,这不显示是合理的,但是这个用户能执行这个操作
				}
				list.add(m);
			}
		}
		return allMenuInOrder;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值