shiro初次使用总结

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

先总结一下使用shiro的原因

shiro的特点及功能

  1. 将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。
  2. 可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro
  3. spring security依赖spring运行,而shiro就相对独立
  4. 它可以实现如下的功能:
    4.1. 验证用户
    4.2.对用户执行访问控制,如:判断用户是否具有角色admin,判断用户是否拥有访问的资源权限。
    4.3.在任何环境下使用SessionAPI。例如C/S程序
    4.4.可以使用多个用户数据源。例如一个是Oracle数据库,另外一个是MySQL数据库。
    4.5.单点登录(SSO)功能
    4.6."Remember Me"服务,类似于购物车的功能,shiro官方建议开启。

shiro组成简介

shiro的4大组成部分——身份认证,授权,会话管理和加密
Authentication:身份验证(身份认证),简称"登录"。
Authorization:授权,给用户给用户分配角色或者权限资源。
Session Manager:用户Session管理器,可以让C/S程序也使用Session来控制权限。
Cryptography:将JDK中复杂的密码加密方式进行封装。

其它相关内容

这里写图片描述

shiro在代码中的使用

pom.xml

shiro在maven中使用的包

<!-- shiro -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.4.0</version>
</dependency>
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>

web.xml

添加shiro过滤器

<!-- shiro过滤器 start -->
<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	<!-- 设置true由servlet容器控制filter的生命周期 -->
	<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>
<!-- shiro 过滤器 end -->

spring-shiro.xml

shiro配置文件(和spring结合)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	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">
	<!--开启shiro的注解 -->
	<bean id="advisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
		<property name="proxyTargetClass" value="true"></property>
	</bean>
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor" />
	<!--注入自定义的Realm -->
	<bean id="customRealm" class="com.ufgov.util.shiro.CustomRealm"></bean>
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm"></property>
	</bean>

	<!--配置ShiroFilter -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager"></property>
		<!--登入页面 -->
		<property name="loginUrl" value="/login_achieve/login"></property>
		<!--登入成功页面 -->
		<property name="successUrl" value="/home" />
		<property name="filters">
			<map>
				<!--退出过滤器 -->
				<entry key="logout" value-ref="logoutFilter" />
			</map>
		</property>
		<!--URL的拦截 -->
		<property name="filterChainDefinitions">
			<value>
				/share = authc
				/login_achieve/logout = logout
			</value>
		</property>

	</bean>
	<!--自定义退出LogoutFilter -->
	<bean id="logoutFilter" class="com.ufgov.util.shiro.SystemLogoutFilter">
		<property name="redirectUrl" value="/login_achieve/login" />
	</bean>
</beans>

引入shiro的配置文件

可以在web.xml,也可以在spring的配置文件中。我配置在spring的配置文件applicationContext.xml中

 <!--  引入shiro-->
 <import resource="spring-shiro.xml"></import>

在spring mvc的中配置内容

spring-mvc.xml中需要配置一些跳转,但是我还没有测试到


	<!-- shiro相关配置 -->
	<!-- 未认证或未授权时跳转必须在springmvc里面配,spring-shiro里的shirofilter配不生效 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--表示捕获的异常 -->
                <prop key="org.apache.shiro.authz.UnauthorizedException">
                    <!--捕获该异常时跳转的路径 -->
                    /403
                </prop>
                <!--表示捕获的异常 -->
                <prop key="org.apache.shiro.authz.UnauthenticatedException">
                    <!--捕获该异常时跳转的路径 -->
                    /403
                </prop>
            </props>
        </property>
    </bean>

CustomRealm

CustomRealm我的理解主要是重写shiro的认证和授权

package com.ufgov.util.shiro;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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.springframework.stereotype.Component;

/**
 * Realm:域,
 * Shiro从Realm获取安全数据(如用户、角色、权限),
 * 就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;
 * 也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource , 即安全数据源。
 * @author Administrator
 *
 */
@Component
public class CustomRealm extends AuthorizingRealm {
	/**
	 * 授权
	 * 该方法尚未启用,因为之前没有使用shiro,所以部分内容还需要修改
	 * @param principalCollection 这个可以理解为当事人的信息!
	 * @return
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		String userName = (String) principalCollection.getPrimaryPrincipal();
		List<String> permissionList = new ArrayList<String>();
		permissionList.add("user:add");
		permissionList.add("user:delete");
		if (userName.equals("zhou")) {
			permissionList.add("user:query");
		}
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.addStringPermissions(permissionList);
		info.addRole("admin");
		return info;
	}

	/**
	 * 认证。这次身份认证会委托给Security Manager,
	 * 而Security Manager又会委托给Authenticator,
	 * 接着Authenticator会把传过来的token再交给我们自己注入的Realm进行数据匹配从而完成整个认证
	 * 最直白的是subject.login(token)会来调用这个方法
	 * 
	 * @param authenticationToken
	 * @return null表示没有找到认证信息,换句话说,也就是根据账号没有找到用户信息
	 * @throws AuthenticationException
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
			throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
		String userAccount = (String) token.getUsername();
		char[] password = token.getPassword();

		return new SimpleAuthenticationInfo(
 				userAccount, // 用户名
 				password, // 密码
 				getName() // realm name
 		);
	}
}

SystemLogoutFilter

继承shiro的LogoutFilter用来在注销时释放资源

package com.ufgov.util.shiro;

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

import org.apache.shiro.session.SessionException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ufgov.entity.SysLoginLog;
import com.ufgov.entity.SysUserInfo;
import com.ufgov.service.SysLoginLogService;
import com.ufgov.service.SysOnceLoginLogService;
import com.ufgov.util.Constans;
import com.ufgov.util.CookieUtil;
import com.ufgov.util.DateUtils;
import com.ufgov.util.MemcachedUtils;
import com.ufgov.util.ToolClass;

@Service
public class SystemLogoutFilter extends LogoutFilter {

	@Autowired
	private SysLoginLogService sysLoginLogServiceImpl;
	@Autowired
	private SysOnceLoginLogService sysOnceloginLogServiceImpl;
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        //在这里执行退出系统前需要清空的数据
    	Subject subject = getSubject(request, response);
    	//这个对应配置文件中配置logoutFilter的redirectUrl
        String redirectUrl = getRedirectUrl(request, response, subject);
        
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse rep = (HttpServletResponse) response;
        
		SysUserInfo sysUserInfo = MemcachedUtils.getUserInfo(req);
		String loginLogToken = Constans.LOGIN_LOG_TOKEN + sysUserInfo.getAccount();
		Object logId = MemcachedUtils.get(loginLogToken);
		if(logId != null){
			SysLoginLog sysLoginLog = new SysLoginLog();
			sysLoginLog.setLogId(logId.toString());
			sysLoginLog.setLogoutTime(DateUtils.getTime());
			sysLoginLogServiceImpl.updateByPrimaryKeySelective(sysLoginLog);
		}
		MemcachedUtils.clearUserInfo(req);
		String tokenId = MemcachedUtils.getTokenId(req);
		sysOnceloginLogServiceImpl.updatelastOut(ToolClass.getIp(req));// 更新最后登出时间
		MemcachedUtils.delete(Constans.TOKEN_ID_ROLE_URL + tokenId);
		MemcachedUtils.delete(loginLogToken);

		CookieUtil.removeCookie(rep,tokenId);
        
        try {
            subject.logout();
        } catch (SessionException ise) {
           ise.printStackTrace();
        }
        issueRedirect(request, response, redirectUrl);
        //返回false表示不执行后续的过滤器,直接返回跳转到登录页面
        return false;
    }
}

LoginController

摘抄一些登录的相关方法


/**
 * Created by Qk on 2017/2/28.
 */

@Controller
@RequestMapping(value = "/login_achieve")
public class LoginController {
......

	/**
	 * @Author Qk
	 * @Date 2017/3/1 9:43
	 * @Description 用户登录
	 * @Param
	 * @Return
	 */
	@RequestMapping(method = RequestMethod.POST, value = "/info")
	@ResponseBody
	public ResponseState login(HttpServletRequest request, HttpServletResponse response) throws Exception {
		ResponseState state = new ResponseState();
		//2018/1/31 lihhz 如果超时,删除user/cookie等
		long noOptTimeOut = Long.parseLong(Constans.NO_OPT_TIME);
		String key = Constans.NO_OPT_TIME_STR + CookieUtil.getUid(request, Constans.TOKEN_ID);
		long lastOptTime = MemcachedUtils.get(key) != null ? (Long) MemcachedUtils.get(key) : 0;
		long thisOptTime = (new Date()).getTime();
		if (thisOptTime - lastOptTime > noOptTimeOut) {
			//删除这个key,因为如果Memcached的超时时间远大于无操作设置时间,会出错
			MemcachedUtils.delete(key);
			String userKey = Constans.USER_INFO + CookieUtil.getUid(request, Constans.TOKEN_ID);
//			CookieUtil.removeCookie(response,Constans.TOKEN_ID);
			MemcachedUtils.delete(userKey);
		} else {
			SysUserInfo userInfo = MemcachedUtils.getUserInfo(request);
			if (userInfo != null) {
				//已经登录调到控制台
				state.setData(Constans.LOGIN_SUCCESS_URL);
				return state;
			}
		}
		String userAccount = request.getParameter("userAccount");
		String password = request.getParameter("password");
		String code = request.getParameter("code");
		
		// 账号为空,密码为空
		if (StringUtils.isEmpty(userAccount) || StringUtils.isEmpty(password)) {
			state.setSuccess(false);
			state.setMessage("用户名或密码错误");
			return state;
		}
		// 验证码是否正确
		String picId = CookieUtil.getUid(request, "PICID");// cookie中获取验证码id
		// 验证码或者cookie中的验证码id为空
		if (StringUtils.isBlank(picId) || StringUtils.isBlank(code)) {
			MemcachedUtils.deleteAfterGet(picId);
			state.setSuccess(false);
			state.setMessage("没有获取到验证码!");
			return state;
		}

		String picCode_true = (String) MemcachedUtils.deleteAfterGet(picId);
		// 验证码失效
		if (StringUtils.isBlank(picCode_true)) {
			state.setSuccess(false);
			state.setMessage("验证码失效!");
			return state;
		}
		if (!picCode_true.toLowerCase().equals(code.toLowerCase())) {
			state.setSuccess(false);
			state.setMessage("验证码输入有误或超时过期");
			return state;
		}
		// 登陆次数超过5次及正确性校验
		state = loginService.loginValidate(userAccount, password);
		if (!state.getSuccess()) {
			return state;
		}
		SysUserInfo sysUserInfo = loginService.getSysUserInfo(userAccount);
		//如果是CA用户,且必须同统一门户登录验证
		if ("yes".equals(loginoss) && sysUserInfo != null && "1".equals(sysUserInfo.getCaAccount())) {
			state.setSuccess(false);
			state.setMessage("请您从统一门户登陆");
			return state;
		}
		
        
		doLogin(request, response, CookieUtil.getUid(request, Constans.TOKEN_ID), sysUserInfo);
        SecurityUtils.getSubject().login(new UsernamePasswordToken(userAccount, password));
		state.setData(Constans.LOGIN_SUCCESS_URL);
		return state;
	}
......
}

总结

事实上,这里只用到了shiro的登录和注销,没有涉及到权限方面的内容。后续再补充。

猜你喜欢

转载自blog.csdn.net/qq_30682027/article/details/81538045