shiro权限框架中的认证和授权过程

本文介绍了Apache Shiro框架在Web环境中的认证和授权流程。默认认证过滤器为表单登录,未认证用户会被重定向到/login。授权方式包括编程式、注解式和JSP标签。注解方式需要在SpringMVC配置中启用,JSP标签使用需引入Shiro标签库,并通过<shiro:hasPermission>和<shiro:hasRole>进行权限检查。
摘要由CSDN通过智能技术生成
</pre><pre name="code" class="html"><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login"/> 
        <property name="successUrl" value="/first" />
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
            	<!-- 对静态资源不需要进行认证 -->
            	/images/** = anon
            	/js/** = anon
            	/styles/** = anon
            	<!-- 对所有url都需要进行认证 -->
                /logout = logout
                /** = authc
            </value>
        </property>
    </bean>

首先看一下Shiro中的web filter过滤器:

         默认采用的认证过滤器filter是表单过滤器,默认登录的url是/login(只要没有认证的都会跳转到/login路径下),辅助登录成功url是/first。


默认登录url跳转到的页面是login.jsp如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="/WEB-INF/jsp/tag.jsp"%>
<html>
<head>
<TITLE>药品采购平台</TITLE>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<LINK rel="stylesheet" type="text/css" href="${baseurl}styles/style.css">
<LINK rel="stylesheet" type="text/css" href="${baseurl}styles/login.css">
<LINK rel="stylesheet" type="text/css"	href="${baseurl}js/easyui/themes/default/easyui.css">
<LINK rel="stylesheet" type="text/css"	href="${baseurl}js/easyui/themes/icon.css">

<STYLE type="text/css">
.btnalink {
	cursor: hand;
	display: block;
	width: 80px;
	height: 29px;
	float: left;
	margin: 12px 28px 12px auto;
	line-height: 30px;
	background: url('${baseurl}images/login/btnbg.jpg') no-repeat;
	font-size: 14px;
	color: #fff;
	font-weight: bold;
	text-decoration: none;
}
</STYLE>
<%@ include file="/WEB-INF/jsp/common_js.jsp"%>

<script type="text/javascript">

	//登录提示方法
	function loginsubmit() {
		$("#loginform").submit();

	}
	
</SCRIPT>
</HEAD>
<BODY style="background: #f6fdff url(${baseurl}images/login/bg1.jpg) repeat-x;">
	<FORM id="loginform" name="loginform" action=""
		method="post">
		<DIV class="logincon">

			<DIV class="title">
				<IMG alt="" src="${baseurl}images/login/logo.png">
			</DIV>

			<DIV class="cen_con">
				<IMG alt="" src="${baseurl}images/login/bg2.png">
			</DIV>

			<DIV class="tab_con">

				<input type="password" style="display:none;" />
				<TABLE class="tab" border="0" cellSpacing="6" cellPadding="8">
					<TBODY>
						<TR>
							<TD>用户名:</TD>
							<TD colSpan="2"><input type="text" id="usercode"
								name="username" style="WIDTH: 130px" /></TD>
						</TR>
						<TR>
							<TD>密 码:</TD>
							<TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" />
							</TD>
						</TR>
						<%-- <TR>
							<TD>验证码:</TD>
							<TD><input id="randomcode" name="randomcode" size="8" /> <img
								id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
								width="56" height="20" align='absMiddle' /> <a
								href=javascript:randomcode_refresh()>刷新</a></TD>
						</TR> --%>

						<TR>
							<TD colSpan="2" align="center"><input type="button"
								class="btnalink" οnclick="loginsubmit()" value="登  录" />
								<input type="reset" class="btnalink" value="重  置" /></TD>
						</TR>
					</TBODY>
				</TABLE>

			</DIV>
		</DIV>
	</FORM>
</BODY>
</HTML>

form过滤器有个特点就是,只要是表单提交(条件:1.post   2.action路径为"")就相当于:

Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);

他会自动到Real中的方法进行身份认证:

/**
	 * 身份认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String userName = (String) token.getPrincipal();
		User user = userService.findByUsername(userName);
		
		if(user == null) {
			//抛出用户不存在异常
			throw new UnknownAccountException();//没找到帐号
		}
		if(user.getLocked()) {
			//抛出用户被锁定异常
			throw new LockedAccountException(); //帐号锁定
		}
		// 如果查询到返回认证信息AuthenticationInfo
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, user.getPassword(),ByteSource.Util.bytes(user.getCredentialsSalt()),
				this.getName());

		return simpleAuthenticationInfo;
	}

值得注意的是SimpleAuthenticationInfo这个方法的构造函数,因为它决定了凭证认证的方式:

1.

   public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName) {
        this.principals = new SimplePrincipalCollection(principal, realmName);
        this.credentials = credentials;
    }

该构造器对应的默认任凭类,什么都不需要输入,没有加密算法,没有迭代次数,直接通过用户名和密码进行进行验证就可以。

<bean id="userRealm" class="com.lgy.web.shiro.UserRealm">
    	<!-- 设置认证凭证器 -->
    	<!--<property name="credentialsMatcher" ref="credentialsMatcher" /> -->
    </bean>


2.

  public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
        this.principals = new SimplePrincipalCollection(principal, realmName);
        this.credentials = hashedCredentials;
        this.credentialsSalt = credentialsSalt;
    }

这个和你加密的密码salt有关:

package com.lgy.service;

import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.lgy.model.User;
	
@Service
public class PasswordHelper {
    private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();

    @Value("${password.algorithmName}")
    private String algorithmName;
    @Value("${password.hashIterations}")
    private int hashIterations;

    public void encryptPassword(User user) {

        user.setSalt(randomNumberGenerator.nextBytes().toHex());

        String newPassword = new SimpleHash(
                algorithmName,           //加密算法
                user.getPassword(),      //密码
                ByteSource.Util.bytes(user.getCredentialsSalt()),  //salt盐   username + salt
                hashIterations   //迭代次数
                ).toHex();

        user.setPassword(newPassword);
    }
}

所以需要设置凭证信息:

<!-- Realm实现 -->
    <bean id="userRealm" class="com.lgy.web.shiro.UserRealm">
    	<!-- 设置认证凭证器 -->
    	<property name="credentialsMatcher" ref="credentialsMatcher" />
    </bean>
     
    <!-- 认证凭证器 -->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    	<!-- 算法名称 -->
    	<property name="hashAlgorithmName" value="${password.algorithmName}" />
    	<!-- 迭代次数 -->
    	<property name="hashIterations" value="${password.hashIterations}" />
    </bean>   

若认证通过后,它会跳转到设置的辅助登录成功url是/first。身份认证就到这里结束!


授权过程如下:

shiro授权有三种方式

Shiro 支持三种方式的授权:

1 编程式:通过写if/else 授权代码块完成:

Subject subject =SecurityUtils.getSubject();

if(subject.hasRole(“admin”)) {

//有权限

} else {

//无权限

}

2 注解式:通过在执行的Java方法上放置相应的注解完成:

@RequiresRoles("admin")

public void hello() {

//有权限

}

3.JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:

<shiro:hasRolename="admin">

<!— 有权限—>

</shiro:hasRole>


编程试的不用说了,重点说说注解方式和jsp标签方式:

若使用SpringMVC注解试,需要在SpringMVC的配置文件中配置注解启动:

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

    <aop:config proxy-target-class="true"></aop:config>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
</beans>

在控制器中:

    @RequiresPermissions("user:create")
    @RequestMapping(value = "/create", method = RequestMethod.GET)
    public String showCreateForm(Model model) {
        //...
        return "user/edit";
    }
当进入到这个Controller中的时候,会先进入realm中的:

/**
	 * 授权认证
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		User user = (User) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(user.getUsername()));
        authorizationInfo.setStringPermissions(userService.findPermissions(user.getUsername()));
		return authorizationInfo;
	}

         权限比较可能有如下2个:

 @RequiresPermissions("user:create")
 @RequiresRoles("admin")

1.基于角色的认证

2.基于权限码的认证


若使用jsp标签进行认证:

条件:需要导入<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

页面中

<shiro:hasPermission name="user:update">

......
</shiro:hasPermission>

<shiro:hasRole name="">    

      ......
 </shiro:hasRole>


同上进入该页面中时候,若出现这样的标签,每出现一个都会调用realm中的:

/**
	 * 授权认证
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		User user = (User) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(user.getUsername()));
        authorizationInfo.setStringPermissions(userService.findPermissions(user.getUsername()));
		return authorizationInfo;
	}


相当于他们调用了shiro中的:

                Subject subject = SecurityUtils.getSubject();
subject.checkRole("");
subject.checkPermission("");                 


*

shiro的jsp标签

 

Jsp页面添加:

<%@ tagliburi="http://shiro.apache.org/tags"prefix="shiro" %>

 

标签名称

标签条件(均是显示标签内容)

<shiro:authenticated>

登录之后

<shiro:notAuthenticated>

不在登录状态时

<shiro:guest>

用户在没有RememberMe时

<shiro:user>

用户在RememberMe时

<shiro:hasAnyRoles name="abc,123" >

在有abc或者123角色时

<shiro:hasRole name="abc">

拥有角色abc

<shiro:lacksRole name="abc">

没有角色abc

<shiro:hasPermission name="abc">

拥有权限资源abc

<shiro:lacksPermission name="abc">

没有abc权限资源

<shiro:principal>

显示用户身份名称

 <shiro:principalproperty="username"/>     显示用户身份中的属性值

  当然每次这么做可能浪费的性能很不好,需要配置缓存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值