shiro学习:shiro整合springweb项目实现用户登录和退出

在上一篇:shiro整合SpringMVC中,我们已经将shiro和springmvc进行了整合,所以我们在其基础上进行修改。

一、实现原理

使用FormAuthenticationFilter过虑器实现 ,原理如下:

将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl

FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)

FormAuthenticationFilter调用realm传入一个token(username和password)

realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。

如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)

二、自定realm

2.1修改doGetAuthenticationInfo方法

首先我们从token获取用户输入的username,根据用户输入的username去数据库中查找用户的信息,如果查不到用户信息直接返回null,然后进入loginController,因为没有查询到用户信息,就会跳转到登录页面;如果能到查到用户的信息,那么我们就获取用户的密码和盐(shiro的散列算法),最后我们会实例化出一个activeUser对象用来存储用户的菜单和权限信息,然后返回认证信息。

public class CustomRealm extends AuthorizingRealm {
	

	@Resource
	private SysService sysService;
	
	@Override
	public void setName(String name) {
		// TODO Auto-generated method stub
		super.setName("customRealm");
	}
	
	//用于授权
	//Authorization 授权的意思
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//从 principals获取主身份信息
		//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
		ActiveUser activeUser =  (ActiveUser) principals.getPrimaryPrincipal();
		// 根据身份信息获取权限信息
		// 从数据库获取到权限数据
		List<SysPermission> permissionList = null;
		try {
			permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 单独定一个集合对象
		List<String> permissions = new ArrayList<String>();
		if (permissionList != null) {
			for (SysPermission sysPermission : permissionList) {
				// 将数据库中的权限标签 符放入集合
				permissions.add(sysPermission.getPercode());
			}
		}

		// 查到权限数据,返回授权信息(要包括 上边的permissions)
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		// 将上边查询到授权信息填充到simpleAuthorizationInfo对象中
		simpleAuthorizationInfo.addStringPermissions(permissions);

		return simpleAuthorizationInfo;
	}

	//用户认证
	//Authentication 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		//1.第一步从token中取出用户名
		String userCode = (String) token.getPrincipal();
		
		//2.根据用户名查询用户
		SysUser sysUser = null;
		try {
			sysUser = sysService.findSysUserByUserCode(userCode);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//如果查询不到返回null
		if(sysUser == null){
			return null;
		}
		
		//获得密码
		String password = sysUser.getPassword();
		//盐
		String salt = sysUser.getSalt();
		
		// 实例化对象
		ActiveUser activeUser = new ActiveUser();
		activeUser.setUserid(sysUser.getId());
		activeUser.setUsercode(sysUser.getUsercode());
		activeUser.setUsername(sysUser.getUsername());
		
		// 通过server取菜单
		List<SysPermission> menus = null;
		try {
			menus = sysService.findMenuListByUserId(sysUser.getId());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		activeUser.setMenus(menus);
		// 如果找到了返回认证信息Authentication
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password,
				ByteSource.Util.bytes(salt),this.getName());

		return simpleAuthenticationInfo;
	}

}

2.2ActiveUser实体类

public class ActiveUser implements java.io.Serializable {
	private String userid;//用户id(主键)
	private String usercode;// 用户账号
	private String username;// 用户名称

	private List<SysPermission> menus;// 菜单
	private List<SysPermission> permissions;// 权限
2.3修改shiro的配置文件

配置shiro的散列,并将器注入得到realm中(完整的配置文件参考下边)

<!-- 自定义 realm -->
	<bean id="customRealm" class="org.lpl.controller.shiro.CustomRealm">
		<property name="credentialsMatcher" ref="credentialsMatcher" />
	</bean>
	<!-- 凭证匹配器 必须放到realm的下边-->
	<bean id="credentialsMatcher"
		class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<!-- 采用MD5进行加密 -->
		<property name="hashAlgorithmName" value="md5" />
		<!-- 设置散列次数 -->
		<property name="hashIterations" value="1" />
	</bean>

三、修改登录的jsp页面

FormAuthenticationFilter进行拦截的时候,默认情况下他从request中拿到的登录信息是,username和password这两个,如下图

FormAuthenticationFilter拦截住取出request中的username和password,所以我们需要保证jsp页面中请求的参数也要是这样



3.1当然我们也可以修改登录的默认属性(配置文件参考下边)

3.1.1.修改shiro的配置文件(在我们设置的凭证器下边加入)

<!-- 自定义的form验证器 -->
	<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
	<bean id="formAuthenticationFilter" 
	class="org.lpl.controller.shiro.MyFormAuthenticationFilter ">
		<!-- 表单中账号的input名称 -->
		<property name="usernameParam" value="usercode" />
		<!-- 表单中密码的input名称 --> 
		<property name="passwordParam" value="password" />
		<!-- 配置自动登录 -->
		<property name="rememberMeParam" value="rememberMe" />
	 </bean>
在shiroFilter中加入
<property name="securityManager" ref="securityManager" />
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.do" />
		<!-- 认证成功同意跳转到first.action 建议不要配置,shiro自动到上一个请求路径 -->
		<property name="successUrl" value="/index.do" />
		<!-- 通过unauthorizedUrl指定没有权限操作时的权限页面 -->
		<property name="unauthorizedUrl" value="/refuse.jsp" />
		<!-- 自定义filter -->
		<property name="filters">
			<map>
				<!-- FormAuthenticationFilter是基于表单认证的过虑器 -->
				<entry key="authc" value-ref="formAuthenticationFilter" />
			</map>
		</property>
3.1.2创建一个MyFormAuthenticationFilter
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		return super.onAccessDenied(request, response);
	}
}

四、看一下现在shiro的配置文件(本篇讲到的所有配置文件)

我们在shiro的过滤连中配置了/logout.action = logout,就可以实现了退出功能

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.2.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
	
	<!-- web.xml中shiro的filter需要的bean -->
	<!-- Shiro 的Web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.do" />
		<!-- 认证成功同意跳转到first.action 建议不要配置,shiro自动到上一个请求路径 -->
		<property name="successUrl" value="/index.do" />
		<!-- 通过unauthorizedUrl指定没有权限操作时的权限页面 -->
		<property name="unauthorizedUrl" value="/refuse.jsp" />
		<!-- 自定义filter -->
		<property name="filters">
			<map>
				<!-- FormAuthenticationFilter是基于表单认证的过虑器 -->
				<entry key="authc" value-ref="formAuthenticationFilter" />
			</map>
		</property>
		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
		<property name="filterChainDefinitions">
			<value>
			<!-- 对静态资源设置匿名访问 -->
			/images/** = anon
			/js/** = anon
			/styles/** = anon
			<!-- 验证码 -->
			<!-- /validatecode.jsp = anon -->
			<!-- 请求logout.action shiro去清除session 147895-->
			/logout.action = logout
			<!-- 商品查询页面,注释掉该方法,在controller中进行注解配置 -->
		<!-- 	/items/queryItems.action = perms[item:query]
			/items/editItems.action = perms[item:edit] -->
			<!-- 配置记住我或认证通过可以访问的地址 -->
<!-- 			/index.jsp = user
			/first.action = user
			/welcome.jsp = user -->
			<!--/** = authc 所有的url都授权后才能访问  -->
			/** = authc
			<!-- /** 所有的url都可以匿名访问-->
			
			</value>
		</property>
	</bean>
	
	<!-- SecurityManager -->
	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 注入自定义realm -->
		<property name="realm" ref="customRealm" />
	</bean>

	<!-- 自定义 realm -->
	<bean id="customRealm" class="org.lpl.controller.shiro.CustomRealm">
		<property name="credentialsMatcher" ref="credentialsMatcher" />
	</bean>
	<!-- 凭证匹配器 必须放到realm的下边-->
	<bean id="credentialsMatcher"
		class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<!-- 采用MD5进行加密 -->
		<property name="hashAlgorithmName" value="md5" />
		<!-- 设置散列次数 -->
		<property name="hashIterations" value="1" />
	</bean>
	<!-- 自定义的form验证器 -->
	<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
	<bean id="formAuthenticationFilter" 
	class="org.lpl.controller.shiro.MyFormAuthenticationFilter ">
		<!-- 表单中账号的input名称 -->
		<property name="usernameParam" value="usercode" />
		<!-- 表单中密码的input名称 --> 
		<property name="passwordParam" value="password" />
		<!-- 配置自动登录 -->
		<property name="rememberMeParam" value="rememberMe" />
	 </bean>
	 
</beans>

五、修改登录的Controller

因为已经将登录的验证交给了FormAuthenticationFilter,如果登录信息没问题他会直接跳转到我们配置的登录成功页面(如果不进行配置,他会直接跳到上一个请求页面),如果登录过程中出现错误,他会获取错误信息然后进行返回

LoginController用于显示登录表单页面,其中shiro authc拦截器进行登录,登录失败的话会把错误存到shiroLoginFailure属性中,在该控制器中获取后来显示相应的错误信息

/**
	 * 此方法只处理登录失败,如果登录成功shiro会自动跳转到上一个请求路径
	 * @param request
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("login.do")//这个路径要和shiro.xml配置的路径要一样
	public String login(HttpServletRequest request) throws Exception{
		//如果登录不成功
		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");	
		//根据shiro返回的异常类路径判断,抛出异常
		if(exceptionClassName!=null){
			if(UnknownAccountException.class.getName().equals(exceptionClassName)){
				throw new CustomException("账号不存在");
			}else if(IncorrectCredentialsException.class.getName().equals(
					exceptionClassName)){
				throw new CustomException("用户名或密码错误");
			}else if("randomCodeError".equals(exceptionClassName)){
				throw new CustomException("验证码错误");
			}else{
				throw new Exception();
			}
		}
		//登录失败跳进这个页面
		return "login";
	}
4.2登录成功跳转到首页
我们已经在shiro的配置文件中配置了<property name="successUrl" value="/index.do" /> 
如果登录成功将会跳转到对应的Controller中,这样就实现了登录功能
@Controller
public class MainController {
	/**
	 * 跳转到首页
	 * @param model
	 * @return
	 */
	@RequestMapping("index.do")
	public String goIndex(Model model){
		//从shiro的session中获取ActiveUser
		Subject subject = SecurityUtils.getSubject();
		//获取身份信息
		ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
		//通过model传到页面中
		model.addAttribute("activeUser", activeUser);
		return "/first";
	}
	

}
六、退出
由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可
具体的配置 参考上边的配置文件
<!-- 请求logout.action shiro去清除session 147895-->
			/logout.action = logout





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值