一、背景

  其实很早的时候,就在项目中有使用到shiro做登陆认证,直到今天才又想起来这茬,自己抽空搭了一个spring+springmvc+mybatis和shiro进行集成的种子项目,当然里面还有很简单的测试。本文将讲述在maven下如何进行集成,希望对你有所帮助,喜欢请推荐。至于shiro相关的,最近也会写几篇介绍的,希望能够有一个主观的了解。

二、集成步骤

  说明:关于spring+springmvc+mybatis的集成请移步另一篇博客:Spring+SpringMvc+Mybatis框架集成搭建教程

  1.第一步引入shiro依赖

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

  2.在web.xml中引入shiro的filter

<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  3.resources文件下的spring目录下新建spring-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
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"> <description>Shiro Configuration</description> <!--custom myself realm-->
<bean id="customRealm" class="com.hafiz.www.shiro.CustomRealm"/> <!--Shiro`s main business-tier object for web-enable applications-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm"/>
</bean> <!--shiro filter-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login.html"/>
<property name="successUrl" value="/index.html"/>
<property name="unauthorizedUrl" value="/unauthorized.html"/>
<property name="filters">
<util:map>
<entry key="auth">
<bean class="com.hafiz.www.filter.AuthorizeFilter"/>
</entry>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/login.json = anon
/logout.json = anon
/js/** = anon
/ = authc
/** = auth
</value>
</property>
</bean>
</beans>

  4.新建自定义的Realm,CustomRealm.java

package com.hafiz.www.shiro;

import com.hafiz.www.po.UserEntity;
import com.hafiz.www.service.UserService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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.beans.factory.annotation.Autowired; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; /**
* Desc:自定义Realm
* Created by hafiz.zhang on 2017/7/21.
*/
public class CustomRealm extends AuthorizingRealm{ private static final String _WILDCARD = "*";
private static final String _PATTERN_APPEND = "+.*"; @Autowired
private UserService userService; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 如果项目不需要授权,则该方法直接 return null;
UserEntity operator = (UserEntity) principalCollection.getPrimaryPrincipal();
//获取该用户具有的所有的角色资源(把null换成findResourceUrlById())
List<String> resourceList = null;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> allPermissions = new HashSet<>(resourceList);
allPermissions.remove("");
allPermissions.remove(null);
List<String> patternPermissions = new ArrayList<>();
//通配url,以*,或者.*
if (CollectionUtils.isNotEmpty(allPermissions)) {
for (String per : allPermissions) {
if (per.endsWith(_WILDCARD)) {
patternPermissions.add(per);
}
}
}
if (CollectionUtils.isNotEmpty(patternPermissions)) {
allPermissions.removeAll(patternPermissions);
for (String pat : patternPermissions) {
if(pat.endsWith(_WILDCARD)){
info.addObjectPermission(new CustomRegexPermission(pat.substring(0,pat.length()-1)+_PATTERN_APPEND));
}else{
info.addObjectPermission(new CustomRegexPermission(pat+_PATTERN_APPEND));
}
}
}
info.addStringPermissions(allPermissions);
return info;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
List<UserEntity> users = userService.getByUserName(username);
if (CollectionUtils.isEmpty(users)) {
throw new UnknownAccountException();
}
if (users.size() != 1) {
throw new LockedAccountException("用户名重复,请联系技术");
}
UserEntity user = users.get(0);
username = user.getUserName();
String password = user.getPassword();
// 第一个参数也可以放user对象,这样在doGetAuthorizationInfo()方法中可以直接使用
return new SimpleAuthenticationInfo(username, password, getName());
}
}

说明:doGetAuthorizationInfo()是做授权,比如项目中有很多资源,指定角色的人员只有指定的资源,这种情况可以使用这个方法来做授权,doGetAuthenticationInfo()方法做认证,我们一般是用作用户登陆主逻辑,这个方法中我们只需要根据用户提供的用户名去数据库中查找对应的用户信息,然后用该信息返回一个SimpleAuthenticationInfo对象即可,不需要比较数据库中的密码和token中的密码是否一直,因为在登陆时shiro会帮我们做这件事,不匹配会抛出IncorrectCredentialsException来提示密码错误。

  5.自定义AuthroizeFilter.java

package com.hafiz.www.filter;

import com.alibaba.fastjson.JSON;
import com.hafiz.www.consts.AppConst;
import com.hafiz.www.vo.JsonResult;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.Writer; /**
* Desc:认证验证过滤器
* Created by hafiz.zhang on 2017/7/21.
*/
public class AuthorizeFilter extends AuthorizationFilter { @Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Subject subject = getSubject(request, response);
if (!subject.isAuthenticated()) {
String requestURI = request.getRequestURI();
if (requestURI.endsWith(".json")) {
JsonResult jr = new JsonResult();
jr.setCode(AppConst.UNAUTHORIZED);
jr.setMsg("登陆超时,请重新登录");
response.setStatus(AppConst.UNAUTHORIZED);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
Writer w = response.getWriter();
w.write(JSON.toJSONString(jr));
w.flush();
w.close();
} else {
response.sendRedirect(request.getContextPath() + "/login.html");
}
return false;
}
Boolean isAjax = isAjax(request);
if (subject.getPrincipal() != null && isAjax) {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
response.setStatus(AppConst.UNAUTHORIZED);
Writer w = response.getWriter();
JsonResult jr = new JsonResult();
jr.setCode(AppConst.UNAUTHORIZED);
jr.setMsg("无权限操作!");
w.write(JSON.toJSONString(jr));
w.flush();
w.close();
return false;
}
return super.onAccessDenied(servletRequest, servletResponse);
} /**
* 根据请求头判断是不是ajax请求
*
* @param request 请求实体
*
* @return
*/
private Boolean isAjax(HttpServletRequest request) {
Boolean isAjax = request.getHeader("X-Requested-With") != null
&& "XMLHttpRequest".equals( request.getHeader("X-Requested-With").toString());
return isAjax;
} /**
* 判断用户是否可以访问请求的资源
*
* @param request 用户请求
*
* @param response 响应
*
* @param mappedValue
*
* @return
*
* @throws Exception
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
Object mappedValue) throws Exception {
// 登陆请求直接放行
if (isLoginRequest(request, response)) {
return true;
} // 获取用户认证信息
Subject subject = getSubject(request, response);
if (!subject.isAuthenticated()) {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpSession session = httpServletRequest.getSession();
String requestUrl = httpServletRequest.getRequestURL().toString();
session.setAttribute(AppConst.LAST_URL_KEY, requestUrl);
return false;
} // 判断请求资源是否授权(如果项目不需要授权,下面省略,直接return true,否则加上下面注释掉的代码,然后最后return false;)
/*String resource = getPathWithinApplication(request);
if (subject.isPermitted(resource)) {
return true;
}*/
return true;
}
}

6.自定义SessionUtils.java来管理shiro相关的session等

package com.hafiz.www.shiro;

import com.hafiz.www.po.UserEntity;
import com.hafiz.www.vo.JsonResult;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* Desc: 用户Session工具类
* Created by hafiz.zhang on 2017/7/22.
*/
public class SessionUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SessionUtils.class); /**
* 获取登陆的key,即用户名
*
* @return
*/
public static String getLoginKey() {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
return (String)subject.getPrincipal();
}
return null;
} /**
* 获取当前登陆用户
*
* @return
*/
public static UserEntity getLoginUser() {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
Session session = subject.getSession();
Object loginUser = session.getAttribute("loginUser");
return loginUser == null ? null : (UserEntity)loginUser;
}
return null;
} /**
* 获取当前登陆用户id
*
* @return
*/
public static Long getLoginUserId() {
UserEntity user = getLoginUser();
if (user != null) {
return user.getId();
}
return null;
} /**
* 获取当前用户是否登陆
*
* @return
*/
public static Boolean isLoggedIn() {
boolean isLoggedIn = SecurityUtils.getSubject().isAuthenticated();
return isLoggedIn;
} public static JsonResult login(String userName, String password) {
Subject subject = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken(userName, password);
JsonResult jr = new JsonResult();
try {
subject.login(token);
} catch (UnknownAccountException ue) {
LOGGER.error("用户不存在:{}", userName);
jr.setSuccess(false);
jr.setMsg("没有该账号");
} catch (LockedAccountException le) {
LOGGER.error("用户名重复");
jr.setSuccess(false);
jr.setMsg("用户名重复,请联系技术");
} catch (IncorrectCredentialsException ie) {
LOGGER.error("密码错误");
jr.setSuccess(false);
jr.setMsg("密码错误");
} catch (Exception e) {
LOGGER.error("登陆出错:{}", e.getLocalizedMessage());
jr.setSuccess(false);
jr.setMsg("登陆出错:" + e.getLocalizedMessage());
}
return jr;
} /**
* 用户退出登陆
*/
public static void logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
}
}

经过上面这些步骤,我们就完成了spring和shiro的集成,关于简单的测试程序,不再贴出,在这里提供该种子项目的github地址:https://github.com/hafizzhang/spring-shiro.git

三、总结

  通过本文,我们就完成了spring集成shiro做登陆的授权和认证,其实很简单,继续努力成长!

Spring集成shiro做登陆认证的更多相关文章

  1. Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

    本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 ...

  2. spring集成shiro报错解决(no bean named 'shiroFilter' is defined)

    引言: 本人在使用spring集成shiro是总是报“no bean named 'shiroFilter' is defined”,网上的所有方式挨个试了一遍,又检查了一遍, 还是没有解决,最后,抱 ...

  3. springboot集成shiro实现权限认证

    github:https://github.com/peterowang/shiro 基于上一篇:springboot集成shiro实现身份认证 1.加入UserController package ...

  4. Spring与Shiro整合 登陆操作

    Spring与Shiro整合 登陆操作 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 编写登陆Controller方法  讲解: 首先,如果你登陆失败的时候,它会把你的异常信息丢到 ...

  5. Spring整合Shiro做权限控制模块详细案例分析

    1.引入Shiro的Maven依赖 <!-- Spring 整合Shiro需要的依赖 --> <dependency> <groupId>org.apache.sh ...

  6. Shiro学习总结(10)——Spring集成Shiro

    1.引入Shiro的Maven依赖 [html] view plain copy <!-- Spring 整合Shiro需要的依赖 --> <dependency> <g ...

  7. shiro实战系列(十五)之Spring集成Shiro

    Shiro 的 JavaBean 兼容性使得它非常适合通过 Spring XML 或其他基于 Spring 的配置机制.Shiro 应用程序需要一个具 有单例 SecurityManager 实例的应 ...

  8. spring 集成shiro 之 自定义过滤器

    在web.xml中加入 <!-- 过期时间配置 --> <session-config><session-timeout>3</session-timeout ...

  9. shiro的登陆认证(shiro项目中来的一)

    一,图解 二,流程 2.1,创建token令牌,token中有用户提交的认证信息即账号和密码 Subject subject = SecurityUtils.getSubject(); Usernam ...

随机推荐

  1. P4891 序列

    P4891 序列 题目描述 给定两个长度为 n 的序列 A 和 B,定义序列 \(C_i=\max\limits_{j=1}^i A_j\) 定义当前的价值是 $\prod\limits_{i=1}^ ...

  2. main函数和线程的关系

    https://github.com/mynawang/Java-Multi-Thread-Learning/blob/master/src/main/java/com/sedion/mynawang ...

  3. 《深入理解java虚拟机》第六章 类文件结构

    第六章 类文件结构   6.2 无关性的基石 各种不同平台的虚拟机与所有的平台都统一使用的程序存储格式--字节码(ByteCode)是构成平台无关性的基石.java虚拟机不和包括java在内的任何语言 ...

  4. asp.net mvc url应用

    //url加密与解密string res1 = HttpUtility.UrlEncode("7Z2K5Lgk/iI="); //值是7Z2K5Lgk%2fiI%3d string ...

  5. pyqt5-多线程QThread类

    要实现多线程,我们要先继承QThread类并重新实现其中的run()函数,也就是说把耗时的操作放入run()函数中 import sys from PyQt5.QtCore import Qt, QT ...

  6. [C++]指针与引用(定义辨析)

    1.定义:     1.1 &-----取地址运算符         功能:返变量的内存地址        Eg:int *p,m;  定义p为指向int类型变量的指针,同时定义变量m     ...

  7. 啊金学习javascript系列一之javascript整体印象

    javascript是一门编程语言,这个是第一个观点.是编程语言,那就拥有编程语言的功能.在我理解之中,编程语言是和计算机打交道的语言,就是我们跟计算机说话用的语言,是用来指挥计算机的.人类能够理解语 ...

  8. python基础知识~配置文件模块

    一 配置文件模块   import ConfigParser ->导入模块  conf = ConfigParser.ConfigParser() ->初始化类二 系统函数  conf.r ...

  9. mysql gtid 第一篇

    GTID1 简介   就是全局事务ID(global transaction identifier )2 构成   uuid+transaction_id 3 格式  7a07cd08-ac1b-11 ...

  10. Jquery对当前日期的操作(格式化当前日期)

    // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占 ...