Detailed explanation of shiro and spring integration and solutions for shiro annotations in the spring project not to take effect

One, shiro configuration in spring project

(1) web.xml configuration

<!-- shiro过滤器 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

(2) Shiro and spring integrated configuration

<!-- 使用shiro安全检查注解 -->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" />
	
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>

	<!-- shiro的生命周期处理器 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<!-- shiro自带的密码匹配器(用来校验密码足够了) -->
	 <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.SimpleCredentialsMatcher"></bean>  
	<!-- security datasource: -->
	<bean id="myRealm" class="cc.eguid.service.shiro.MyRealm">
		<property name="credentialsMatcher" ref="credentialsMatcher"/><!-- 密码匹配器 -->
        <property name="cachingEnabled" value="false"/><!-- 禁止缓存 -->
	</bean>
	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myRealm" />
	</bean>
	<!-- shiro过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 配置安全管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 身份认证失败跳转的地址 -->
		<property name="loginUrl" value="/login/" />
		<!-- 身份认证成功跳转的地址 -->
		<property name="successUrl" value="/" />
		<!-- 权限认证失败跳转的地址 -->
		<property name="unauthorizedUrl" value="/login/unauthorized" />
		<property name="filterChainDefinitions">
			<!--anon 表示匿名访问,不需要认证以及授权 -->
			<!--authc表示需要认证 没有进行身份认证是不能进行访问的 -->
			<!--authc,roles[admin]表示是admin角色的用户才能访问 -->
			<value>
				/static/** = anon
				/login/** = anon
				/common/** = anon
				/admin/** = authc,roles[admin]
				/* = authc
				/** = authc
			</value>
		</property>
	</bean>
	
	

Two, realm and custom password checker implementation

1. Realm implementation

public class MyRealm extends AuthorizingRealm{
	Logger log=Logger.getLogger(MyRealm.class);
	
	@Autowired
    private UserService userService;//这是自己实现的用户信息操作类,实现用户信息,用户角色信息、用户权限信息查询功能

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		UserInfo user = (UserInfo) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		// 查询角色信息
		Collection<String> roles = userService.findRoles(user);
		info.addRoles(roles);
		log.info("shiro获取用户所属角色列表:"+roles);
		// 查询权限信息
		Collection<String> permissions = userService.findPermissions(user.getSystemuserid());
		info.addStringPermissions(permissions);
		log.info("shiro获取用户权限列表:"+permissions);
		return info;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException{
		//用户输入的用户名密码
		String loginname=  token.getPrincipal().toString();
		Object password=token.getCredentials();
		log.info("shiro正在处理尝试登录的用户信息:"+loginname+",密码:"+new String((char[])password));
		//数据库中的用户信息
		UserInfo user =userService.queryUserInfoByLoginName(loginname);
		if(user==null||CommonUtil.isNull(user.getLoginusername(),user.getPassword(),user.getSystemuserid())){
			return null;
		}
		log.info("shiro获取到当前用户尝试登录的真实数据:"+user.getLoginusername()+",密码:"+user.getPassword());
		//数据库中的正确的账户信息
		AuthenticationInfo accountInfo =new SimpleAuthenticationInfo(user, user.getPassword(),getName());
				
		//自己获取密码验证器(由于shiro实现的密码校验方法是密码错误会直接抛异常,不采用,所以改成直接手动校验)
		CredentialsMatcher matcher=getCredentialsMatcher();
		if(matcher==null){
			log.error("没有配置密码匹配器");
			return null;
		}
		//校验密码
		if(matcher.doCredentialsMatch(token,accountInfo)){
			return accountInfo;//校验通过,返回账号信息
		}
		
		return null;
	}


}

2. Custom password checker


/**
 * 自定义shiro密码匹配(密码是在md5散列值的基础上再次进行md5加盐操作,加盐值不保存在数据库,而是放在配置文件中)
 * @author eguid
 *
 */
public class MyCredentialsMatcher extends CodecSupport implements CredentialsMatcher {
	private static final Logger log = LoggerFactory.getLogger(MyCredentialsMatcher.class);

	protected Object getCredentials(AuthenticationToken token) {
		return token.getCredentials();
	}

	protected Object getCredentials(AuthenticationInfo info) {
		return info.getCredentials();
	}

	@Autowired
	private CommonConfigs commonConfigs;
	/**
	 * 验证密码
	 * 
	 * @param tokenCredentials
	 * @param accountCredentials
	 * @return
	 */
	protected boolean equals(Object tokenCredentials, Object accountCredentials) {
		if (log.isDebugEnabled()) {
			log.debug("Performing credentials equality check for tokenCredentials of type ["
					+ tokenCredentials.getClass().getName() + " and accountCredentials of type ["
					+ accountCredentials.getClass().getName() + "]");
		}
		if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {
			if (log.isDebugEnabled()) {
				log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing "
						+ "array equals comparison");
			}
			byte[] tokenBytes = toBytes(tokenCredentials);
			byte[] accountBytes = toBytes(accountCredentials);
			return MessageDigest.isEqual(tokenBytes, accountBytes);
		} else {
			return accountCredentials.equals(tokenCredentials);
		}
	}
	
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		Object tokenCredentials = getCredentials(token);
		Object accountCredentials = getCredentials(info);
		String account=String.valueOf((char[])tokenCredentials);
		if(commonConfigs.getMd5salt()==null){
			if (log.isDebugEnabled()) {
				log.debug("配置文件中的加盐值为空,无法进行密码匹配,请确认配置文件是否在指定位置或配置指定加盐值");
			}
			return false;
		}
		String saltaccount=MD5Util.getMD5(account, commonConfigs.getMd5salt());
		if (log.isDebugEnabled()) {
			log.debug("加盐后的密码:"+saltaccount);
		}
		return equals(accountCredentials, saltaccount.toCharArray());
	}

}

3. Use of annotations and template tags (where the annotations are invalid)

1. Annotation use

@RequiresPermissions({"user:update:view"})//Check operation permissions

@RequiresPermissions(value={"user:add","user:view"},logical=Logical.OR)//One of the two operation permissions can pass the inspection if the conditions are met

@RequiresRoles({"admin"})//Check roles

@RequiresRoles(value={"debug","admin"},logical=Logical.OR)//One of the two roles can meet the conditions


@RequiresAuthentication//Check whether shiro authentication is passed
@RequiresGuest//No verification required
@RequiresUser//Check whether the user is a user in the current system


2. Label use

To use tags, you need to import shiro's tag library first <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

(1) Display user identity information

<shiro: principal/>
calls Subject.getPrincipal() by default


<shiro:principal property="username"/>

Equivalent to ((User)Subject.getPrincipals()).getUsername()


(2) Displayed by logged-in shiro users

 <shiro:user>  
Welcome [<shiro:principal/>] to log in, <a href="logout">sign out</a>  
< shiro:user>

(3) Anonymous user access

<shiro:guest>Users who have not been verified by shiro (guests, anonymous users) </shiro:guest>  


(4) Those who have logged in at shiro (logged in users)

 <shiro:authenticated>  
    User [<shiro:principal/>] has been authenticated  
< shiro:authenticated> 

(5) Those who have not logged in to shiro

 <shiro:notAuthenticated>
    not authenticated (including remember me)
< shiro:notAuthenticated>

(6) Check the role

 <shiro:hasRole name="admin">
    用户[<shiro:principal/>]拥有角色admin<br/>
< shiro:hasRole>

检查任意角色(其中一个满足条件即通过,相当于OR)
 <shiro:hasAnyRoles name="admin,user">
    用户[<shiro:principal/>]拥有角色admin或user<br/>
<sh iro:hasAnyRoles>

不具有角色(反向判断)
 <shiro:lacksRole name="abc">
    用户[<shiro:principal/>]不具有角色abc<br/>
< shiro:lacksRole>

(7)操作权限判断

 <shiro:hasPermission name="user:create">  
    用户[<shiro:principal/>]拥有权限user:create<br/>  
< shiro:hasPermission>    

No operation authority (reverse judgment)
 <shiro:lacksPermission name="org:create">  
    用户[<shiro:principal/>]没有权限org:create<br/>  
< iro:lacksPermission>  



Four, the annotation does not take effect

Release the shiro annotation to the annotation scanning of springMVC (that is, load it in the springMVC container)

<!-- 使用shiro安全检查注解 -->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" />
	
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>
Scan the above shiro directly and put it in the servlet-context.xml below to make the shiro annotation take effect

<!-- springMVC应用 -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>2</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>






Guess you like

Origin blog.csdn.net/eguid/article/details/78337275