Shiro的Web项目配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jintao_Ma/article/details/72861806

Shiro的Web项目配置

一 shiro的学习

二 shiro的java客户端配置

三 关于权限的一些问题

一 shiro的学习

官网和张开涛博客

二 shiro的java客户端配置

1.在web.xml中配置shiro的过滤器

	<!-- shiro 安全过滤器 -->
	<!-- The filter-name matches name of a 'shiroFilter' bean inside  -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<async-supported>true</async-supported>
		<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>
同时在web.xml读取shiro的配置文件

扫描二维码关注公众号,回复: 3283577 查看本文章

2.在spring-shiro-web.xml中配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:util="http://www.springframework.org/schema/util"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<!-- 	为了获取adminPath的值 -->
	<context:property-placeholder ignore-unresolvable="true" location="classpath*:/system.properties"/>

	<!-- 配置安全管理中心,Shiro的主要业务层对象基于web的应用程序 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm"/>
	</bean>
	
<!-- 	自定义的过滤器,用来验证登陆 -->
	<bean id="formAuthenticationCaptchaFilter" class="com.huaxia.shiro.FormAuthenticationCaptchaFilter">
		<property name="usernameParam" value="username"/>
		<property name="passwordParam" value="password"/>
		<property name="captchaParam" value="captcha"/>
		<property name="loginUrl" value="${adminPath}/login"/>
	</bean>
<!-- 	自定义的过滤器 -->
	<bean id="userFilter" class="com.huaxia.shiro.HuaXiaUserFilter">
	</bean>

<!-- Shiro的Web过滤器,在web.xml中配置的shiroFilter即指向此 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 		指定一个安全管理中心,用来验证用户能否登陆和相关权限 -->
		<property name="securityManager" ref="securityManager"/>
<!-- 		所有地址被重定向到该地址 -->
		<property name="loginUrl" value="${adminPath}/login"/>
<!-- 		用户登录成功后的页面地址 -->
		<property name="successUrl" value="${adminPath}"/>
<!-- 		过滤器链,在shiroFilter之前即开始执行 -->
		<property name="filters">
			<util:map>
				<entry key="authc" value-ref="formAuthenticationCaptchaFilter"/>
				<entry key="user" value-ref="userFilter"/>
			</util:map>
		</property>
<!-- 		配置地址对应的过滤器 -->
		<property name="filterChainDefinitions">
			<value>
				${adminPath}/login = authc
				/** = user
			</value>
		</property>
	</bean>


	<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

	<!-- 下面两个bean是shiro官网推荐的配置  -->
	
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
		<property name="proxyTargetClass" value="true" />
	</bean>
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager"/>
	</bean>
</beans>

其中UserRealm的实现如下:

package com.huaxia.shiro;


import javax.annotation.PostConstruct;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.code.kaptcha.Constants;
import com.huaxia.Constant;
import com.huaxia.common.utils.security.Digests;
import com.huaxia.common.utils.security.Encodes;
import com.huaxia.user.entity.User;
import com.huaxia.user.service.UserService;


@Service
public class UserRealm extends AuthorizingRealm {
    private Logger logger = LoggerFactory.getLogger(UserRealm.class);

    @Autowired
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordCaptchaToken captchaToken = (UsernamePasswordCaptchaToken) token;
        String username = String.valueOf(token.getPrincipal());
        User user = userService.findByUsername(username,Constant.USER_DELFLAG);
        if(null != user && doCaptchValidate(captchaToken)) {
            if (Boolean.TRUE.equals(user.getLocked())) {
                throw new LockedAccountException(); //帐号锁定
            }
            if(Constant.LOGIN_STATUS_N.equals(user.getLoginStatus())){
            	throw new DisabledAccountException();
    		}
            byte[] salt = Encodes.decodeHex(user.getSalt());
            //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,可以自定义实现
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    user.getUserName(),
                    user.getPassword(), //密码
                    ByteSource.Util.bytes(salt),
                    getName()  //realm name
            );
            //SecurityUtils.getSubject().getSession().setAttribute("user", user);
            SecurityUtils.getSubject().getSession().setAttribute("userId", user.getUserId());
            userService.updateByLogin(user);
            return authenticationInfo;
        }else{
            throw new UnknownAccountException();
        }
    }

    protected boolean doCaptchValidate(UsernamePasswordCaptchaToken token){
        String captcha = (String) SecurityUtils.getSubject().getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
        if(captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha())){
            throw new CaptchaException("Code error");
        }
        return true;
    }

    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(Digests.SHA1);
        matcher.setHashIterations(Constant.HASH_INTERATIONS);
        setCredentialsMatcher(matcher);
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
}
其中formAuthenticationCaptchaFilter的实现如下:

package com.huaxia.shiro;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class FormAuthenticationCaptchaFilter extends FormAuthenticationFilter {

	private Logger logger = LoggerFactory.getLogger(FormAuthenticationCaptchaFilter.class);

	public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
	private String captchaParam = DEFAULT_CAPTCHA_PARAM;

	public String getCaptchaParam() {
		return captchaParam;
	}

	public void setCaptchaParam(String captchaParam){
		this.captchaParam = captchaParam;
	}

	protected String getCaptcha(ServletRequest request) {
		return WebUtils.getCleanParam(request, getCaptchaParam());
	}

	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
		String username = getUsername(request) == null ? "" : getUsername(request);
		String password = getPassword(request) == null ? "" : getPassword(request);
		String captcha = getCaptcha(request) == null ? "" : getCaptcha(request);
		boolean rememberMe = isRememberMe(request);
		return new UsernamePasswordCaptchaToken(username,password.toCharArray(), rememberMe, captcha);
	}



	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
									 ServletRequest request, ServletResponse response) throws Exception {
		//		issueSuccessRedirect(request, response);
		//      we handled the success redirect directly, prevent the chain from continuing:
		HttpServletRequest httpServletRequest = (HttpServletRequest)request;
		HttpServletResponse httpServletResponse = (HttpServletResponse)response;


		if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))
				|| request.getParameter("ajax") == null) {// 不是ajax请求
			httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + this.getSuccessUrl());
		} else {
			httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + this.getSuccessUrl());
		}

		return false;
	}




}
其中HuaXiaUserFilter的实现如下:

package com.huaxia.shiro;

import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class HuaXiaUserFilter extends UserFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        /*if(!"XMLHttpRequest".equalsIgnoreCase(WebUtils.toHttp(response).getHeader("X-Requested-With"))
                || request.getParameter("ajax") == null ){
            this.saveRequestAndRedirectToLogin(request, response);
        }else{*/
            HttpServletResponse res = WebUtils.toHttp(response);
            res.setHeader("sessionstatus","timeout");
            //res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
       /* }*/
        this.saveRequestAndRedirectToLogin(request, response);

        return false;
    }

}
3.另外在Mvc的配置文件spring-mvc.xml中加入如下拦截器内容:
	<aop:config proxy-target-class="true"></aop:config>
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager"/>
	</bean>

三 关于权限的一些问题

1.shiro如何实现验证码?

关于shiro的验证码,使用的是google的kaptcha , 在web.xml中配置sevlet如下:

	<servlet>
		<servlet-name>ImageServlet</servlet-name>
		<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>ImageServlet</servlet-name>
		<url-pattern>/ImageServlet</url-pattern>
	</servlet-mapping>


2.用户名密码通常保存为什么要加盐值?

  由于通常密码保存使用的是md5加密,同样的密码在md5后会产生相同的加密后字符串,如果有数据库的查看权限,那么看到相同的加密后字符串,很容易猜到是相同的密码。同时根据md5对照表(或者一些网站),能够找到密码。 如果密码在加上随机盐后再进行md5,那么同样的密码在md5后的字符串是不同的,就能够避免上面两个问题。总的来说增加了破解的难度。

3.通常情况下用户-角色-权限(资源)之间的关系

用户与角色多对多:一个用户可以拥有多个角色,一个角色可以被多个用户具有

角色与权限(资源,主要指菜单,按钮等)多对多:一个角色可以拥有多个权限,一个权限可以被多个角色拥有

3.1 在首页,通常根据用户查找角色,然后根据角色列出相关的菜单栏和相关按钮

3.2 在系统配置中:

用户配置:可以增删用户,也可以配置用户的多个角色

角色配置:可以配置一个角色的多个权限

权限配置:可以增删相关的资源(菜单或者按钮)

4.shiro授权的几种方式:

4.1 在代码体中:

if (currentUser.hasRole("admin"))

4.2 在方法上:

@RequiresPermissions(“account:create”)‏
public void openAccount( Account acct )

4.3 在jsp页面上:

   <shiro:hasPermission name=“users:manage”>
        <a href=“manageUsers.jsp”> </a>
   </shiro:hasPermission>

Shiro的官方文档整理的感觉差强人意,非常不明朗,需要结合张开涛的博客来看。有很多的地方需要学习,后续更新。

猜你喜欢

转载自blog.csdn.net/Jintao_Ma/article/details/72861806