Shiro的Web项目配置

来源:互联网 发布:魔扣少儿编程网址 编辑:程序博客网 时间:2024/06/05 02:10

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的配置文件

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;@Servicepublic 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);}@Overrideprotected 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的官方文档整理的感觉差强人意,非常不明朗,需要结合张开涛的博客来看。有很多的地方需要学习,后续更新。