记录一些spring-shiro的问题

首先设计用户权限相关的:

总共有用户、角色、权限、用户组、用户角色关系、用户用户组关系、权限关系。

理解:

1.一个用户可以有多个角色,一个角色可以被多个用户拥有,多对多关系拆分到用户角色关系。

2.一个用户可以属于多个用户组,一个用户组可以有多个用户,多对多关系拆分到用户用户组关系。

3.一个权限可以有多个用户、角色或者用户组,一个用户、角色或者用户组可以有多个权限,拆分到权限关系。

如图(请忽略作图水平,用画图画的……):


算了,不多说了,附上表的sql:

-- 权限表
DROP TABLE IF EXISTS `fms_permission`;
CREATE TABLE IF NOT EXISTS `fms_permission` (
  `id` varchar(64) NOT NULL,
  `permission_name` varchar(1000) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 角色、用户和用户组与权限的关系
DROP TABLE IF EXISTS `fms_permission_relation`;
CREATE TABLE IF NOT EXISTS `fms_permission_relation` (
  `id` varchar(64) NOT NULL,
  `permission_id` varchar(64) NOT NULL,
  `username` varchar(40) DEFAULT NULL,
  `role_id` varchar(64) DEFAULT NULL,
  `group_id` varchar(64) DEFAULT NULL,
  `permission_type` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 角色表
DROP TABLE IF EXISTS `fms_role`;
CREATE TABLE IF NOT EXISTS `fms_role` (
  `id` varchar(64) NOT NULL,
  `role_name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 用户角色关系表
DROP TABLE IF EXISTS `fms_role_relation`;
CREATE TABLE IF NOT EXISTS `fms_role_relation` (
  `id` varchar(64) NOT NULL,
  `username` varchar(40) NOT NULL,
  `role_id` varchar(64) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 用户表
DROP TABLE IF EXISTS `fms_user`;
CREATE TABLE IF NOT EXISTS `fms_user` (
  `username` varchar(40) NOT NULL,
  `nick_name` varchar(50) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 用户组表
DROP TABLE IF EXISTS `fms_user_group`;
CREATE TABLE IF NOT EXISTS `fms_user_group` (
  `id` varchar(64) NOT NULL,
  `group_name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 用户用户组关系表
DROP TABLE IF EXISTS `fms_user_group_relation`;
CREATE TABLE IF NOT EXISTS `fms_user_group_relation` (
  `id` varchar(64) NOT NULL,
  `group_id` varchar(64) NOT NULL,
  `username` varchar(40) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

mybatis的一些映射就不上了,后面会有项目的的地址,权限是公共的可以去看一下。写的比较差,有啥意见啥的还请指点。

好了,不多说了,接下来就是shiro的,首先把maven要的依赖贴上:

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.3.2</version>
</dependency>

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-web</artifactId>
	<version>1.3.2</version>
</dependency>

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.3.2</version>
</dependency>

这里踩过一个坑,就开始我是使用maven repository里面shiro最新的包:1.4.0版本的,后面启动项目直接tomcat报错了,不知道是不是1.4.0的配置有什么改动或者我配置有哪里不正确,报错原因是jar包存在冲突或者jar包没有下载好,但是我用maven test能跑过,期待大佬指点。

接下来就是shiro的配置了,首先先把web.xml要添加的内容贴上:

这里的filter-name等会shiro的配置文件是要使用到的。

<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>shiroFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

spring-shiro.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/mvc/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/mvc/spring-aop.xsd">

	<!-- 权限配置管理器 -->
	<bean id="securityManager"
		class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- ref对应我们写的realm MyShiro -->
		<property name="realm" ref="myShiro" />
		<!-- 使用下面配置的缓存管理器 -->
		<property name="cacheManager" ref="cacheManager" />
	</bean>

	<!-- 配置shiro的过滤器工厂, id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
	<bean id="shiroFilter"
		class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 调用我们配置的权限管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 配置我们的登录请求地址 -->
		<property name="loginUrl" value="/login.jsp" />
		<!-- 配置我们在登录成功后的跳转地址,如果你访问的是非/login地址,则跳转到你访问的地址,不配置默认跳回上一个访问的url -->
		<!-- <property name="successUrl" value="/index" /> -->
		<!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
		<property name="unauthorizedUrl" value="/403" />
		<!-- 权限配置 -->
		<property name="filterChainDefinitions">
			<value>
				<!-- anon表示此地址不需要任何权限即可访问(静态资源) -->
				/images/**=anon
				/js/**=anon
				/css/**=anon
				/bootstrap/css/**=anon
				/bootstrap/fonts/**=anon
				/bootstrap/js/**=anon

				<!-- 登录请求不拦截 -->
				/user/login=anon
				/login.jsp=anon

				<!-- perms[index_view]表示访问此连接需要权限为index_view的用户 -->
				/index.jsp=perms[index_view]

				<!-- roles[manager]表示访问此连接需要用户的角色为manager -->
				<!-- /user/add=roles[manager] /user/del/**=roles[admin] /user/edit/**=roles[manager] -->

				<!-- 注销 -->
				/logout=logout

				<!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
				/** = authc
			</value>
		</property>
	</bean>

	<bean id="cacheManager"
		class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />

	<bean id="lifecycleBeanPostProcessor"
		class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

</beans>

perms[index_view]这里面的index_view是自己定义的权限名称,后面写用户认证的时候看代码就清楚了。

anon代表不拦截,还有些其他的,如logout表示注销用户,shiro会把登录的信息清理掉,其他的可以自己查阅一下。另外还可以自己设置第三方缓存,我这里用的是自带的。


这个需要加入到applicationContext.xml里面的,要在spring监听起来的时候加载的。

<import resource="classpath:pring-shiro.xml"/>

我这里使用的是引入,也可以定义好差不多的后缀,在web.xml那里配置如applicaitonContext-*.xml这样子。


接下来就是 用户认证和登录了:

MyShiro:

package com.xqtion.fms.service.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.xqtion.fms.dao.IUserDao;
import com.xqtion.fms.entity.PermissionRelation;
import com.xqtion.fms.entity.RoleRelation;
import com.xqtion.fms.entity.User;
import com.xqtion.fms.entity.UserGroupRelation;
import com.xqtion.fms.service.IMyShiro;

@Service
@Transactional
public class MyShiro extends AuthorizingRealm implements IMyShiro {

	@Autowired
	private IUserDao userDao;

	/**
	 * 权限认证
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		// 获取登录时输入的用户名
		String loginName = (String) principalCollection.fromRealm(getName()).iterator().next();
		// 去数据库查询用户
		User user = userDao.getUserByUserName(loginName);
		if (null != user) {
			// 权限信息对象info,用来存放查出用户的所有角色(role)及权限(permission)
			SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
			// 用户的角色集合
			Set<String> roles = new HashSet<>();
			// 用户权限的集合
			Set<String> permissions = new HashSet<>();
			// 获取用户角色
			getUserRoleName(user.getRoleRelations(), roles);
			// 获取用户权限
			getUserPermission(user, permissions);
			// 添加角色
			if (!CollectionUtils.isEmpty(roles)) {
				info.setRoles(roles);
			}
			// 添加权限
			if (!CollectionUtils.isEmpty(permissions)) {
				info.addStringPermissions(permissions);
			}
			return info;
		}
		return null;
	}

	/**
	 * 登录验证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
			throws AuthenticationException {

		// UsernamePasswordToken对象用来存放提交的登录信息
		UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
		// 查出是否有此用户
		User user = userDao.getUserByUserName(token.getUsername());
		if (null != user) {
			// 若存在,将此用户放到登录认证的info中
			return new SimpleAuthenticationInfo(user.getUsername(), 
					user.getPassword(), getName());
		}

		return null;
	}

	// 获取用户权限
	private void getUserRoleName(final List<RoleRelation> roleRelations, final Set<String> roles) {
		if (!CollectionUtils.isEmpty(roleRelations)) {
			for (RoleRelation roleRelation : roleRelations) {
				roles.add(roleRelation.getRole().getRoleName());
			}
		}
	}

	// 根据用户本身、角色、用户组获得权限
	private void getUserPermission(final User user, final Set<String> permissions) {

		// 取消用户权限的集合
		Set<String> downPermissions = new HashSet<>();

		// 根据用户
		getUserPermission(user.getPermissionRelations(), permissions, downPermissions);

		// 根据角色
		if (!CollectionUtils.isEmpty(user.getRoleRelations())) {
			for (RoleRelation roleRelation : user.getRoleRelations()) {
				getUserPermission(roleRelation.getRole().getPermissionRelations(), permissions, downPermissions);
			}
		}

		// 根据用户组
		if (!CollectionUtils.isEmpty(user.getUserGroupRelations())) {
			for (UserGroupRelation groupRelation : user.getUserGroupRelations()) {
				getUserPermission(groupRelation.getUserGroup().getPermissionRelations(), permissions, downPermissions);
			}
		}

		// 移除取消的权限
		permissions.removeAll(downPermissions);

	}

	// 根据权限关系获取权限
	private void getUserPermission(final List<PermissionRelation> permissionRelations, final Set<String> permissions,
			final Set<String> downPermissions) {
		if (!CollectionUtils.isEmpty(permissionRelations)) {
			for (PermissionRelation permissionRelation : permissionRelations) {
				if (Objects.equals(permissionRelation.getPermissionType(), "up")) {
					permissions.add(permissionRelation.getPermission().getPermissionName());
				} else {
					downPermissions.add(permissionRelation.getPermission().getPermissionName());
				}
			}
		}
	}

}

这个是根据上面设计的用户权限来实现的,有自己的需求或者想法可以灵活改动,反正就是把角色和权限加进去,验证 用户登录还可以用加密,这里暂时没有加密。

还有controller:

UserController:

package com.xqtion.fms.controller;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.xqtion.fms.entity.User;
import com.xqtion.fms.service.IUserService;

@Controller
@RequestMapping(value = "/user")
public class UserController {

	@Resource
    private IUserService userService;

    @RequestMapping(value="/login2", method=RequestMethod.POST)
    public ModelAndView login2(User model, HttpSession session) {
        User user = userService.login(model);

        if (user == null || !user.getPassword().equals(model.getPassword())) {
            return new ModelAndView("redirect:/login.jsp");
        } else {
            session.setAttribute("user", user);
            ModelAndView mav = new ModelAndView();
            mav.setViewName("redirect:/index.jsp");
            return mav;
        }
    }
    
    @RequestMapping(value="/login", method=RequestMethod.POST)
    public String login3(User user, HttpServletRequest request,BindingResult bindingResult, RedirectAttributes redirectAttributes) {
    	try {
    		if (bindingResult.hasErrors()) {
    			return "redirect:/login.jsp";
    		}
    		// 使用权限工具进行用户登录,登录成功后跳到shiro配置的successUrl中,与下面的return没什么关系!
    		Subject subject = SecurityUtils.getSubject();
    		subject.login(new UsernamePasswordToken(user.getUsername(), user.getPassword()));
    		return "redirect:/" + resolveUrl(WebUtils.getAndClearSavedRequest(request).getRequestUrl());
    	} catch (AuthenticationException e) {
    		redirectAttributes.addFlashAttribute("message", "用户名或密码错误");
    		return "redirect:/login.jsp";
    	}
    }
    
    // 解析url
    private String resolveUrl(String url) {
    	String pattern = "/fms/";
    	String redirectUrl = url.substring(pattern.length(), url.length());
    	System.out.println("============" + redirectUrl);
    	return redirectUrl;
    }
}

这里又有个坑,上面的shiro配置文件不是说,successUrl没有配置的话会自动跳转回上一个页面,亲测无效,不知道是不是漏了配置些什么,再次跪求大佬指点……我这里有点取巧,感觉不太好吧……,是自己拿到shiro存在自己session里面的上一个url,然后自己解出需要的部分去进行重定向的……


好了,讲到这里差不多了,肯定还有很多没讲明白,需要项目源码的可以到我的码云项目地址去看:项目地址

目前写的东西还是很少,后续有时间的话会不断地增加一些新的东西去完善的……最后,再次跪求大佬指点给点建议……


自己总结下:之前使用shiro有些地方一直跟springmvc配置的视图渲染搞不清,现在搞清楚了,只有controller里面返回的才会去触发到springmvc的视图渲染器。直接url访问webapps下面的资源是不影响的,shiro的拦截配置这样子理解。


好了………………

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值