Spring集成shiro做登陆认证
一、背景
其实很早的时候,就在项目中有使用到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做登陆认证的更多相关文章
- Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理
本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 ...
- spring集成shiro报错解决(no bean named 'shiroFilter' is defined)
引言: 本人在使用spring集成shiro是总是报“no bean named 'shiroFilter' is defined”,网上的所有方式挨个试了一遍,又检查了一遍, 还是没有解决,最后,抱 ...
- springboot集成shiro实现权限认证
github:https://github.com/peterowang/shiro 基于上一篇:springboot集成shiro实现身份认证 1.加入UserController package ...
- Spring与Shiro整合 登陆操作
Spring与Shiro整合 登陆操作 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 编写登陆Controller方法 讲解: 首先,如果你登陆失败的时候,它会把你的异常信息丢到 ...
- Spring整合Shiro做权限控制模块详细案例分析
1.引入Shiro的Maven依赖 <!-- Spring 整合Shiro需要的依赖 --> <dependency> <groupId>org.apache.sh ...
- Shiro学习总结(10)——Spring集成Shiro
1.引入Shiro的Maven依赖 [html] view plain copy <!-- Spring 整合Shiro需要的依赖 --> <dependency> <g ...
- shiro实战系列(十五)之Spring集成Shiro
Shiro 的 JavaBean 兼容性使得它非常适合通过 Spring XML 或其他基于 Spring 的配置机制.Shiro 应用程序需要一个具 有单例 SecurityManager 实例的应 ...
- spring 集成shiro 之 自定义过滤器
在web.xml中加入 <!-- 过期时间配置 --> <session-config><session-timeout>3</session-timeout ...
- shiro的登陆认证(shiro项目中来的一)
一,图解 二,流程 2.1,创建token令牌,token中有用户提交的认证信息即账号和密码 Subject subject = SecurityUtils.getSubject(); Usernam ...
随机推荐
- P1169 [ZJOI2007]棋盘制作 && 悬线法
P1169 [ZJOI2007]棋盘制作 给出一个 \(N * M\) 的 \(01\) 矩阵, 求最大的正方形和最大的矩形交错子矩阵 \(n , m \leq 2000\) 悬线法 悬线法可以求出给 ...
- 20190320 Dojo常用方法总结
0. 使用环境 Dojo版本:1.14.2 此次总结以dojo的base为主,即不需要手动引入组件 1. 常用不归类方法 1.1. dojo.addOnLoad 在页面加载完成并且dojo.requi ...
- 注解 和 xml 配置的优缺点【转】
java annotation(注解) 的优点缺点 Annotation和xml各自作为配置项的优点与缺点. Annotation 一.Annotation 的优点 1.保存在 class 文件中,降 ...
- matplotlib笔记——legend用法
rates = [0.01, 0.001, 0.0001] models = {} costs = np.array([[0.7, 0.9, 0.4, 0.6, 0.4, 0.3, 0.2, 0.1] ...
- JAVA迭代器学习--在JAVA中实现线性表的迭代器
1,迭代器是能够对数据结构如集合(ADT的实现)进行遍历的对象.在遍历过程中,可以查看.修改.添加以及删除元素,这是它与一般的采用循环来遍历集合中的元素不同的地方.因为,通常用循环进行的遍历操作一般是 ...
- VUE2.0 饿了吗视频学习笔记(七-终):compute,循环,flex,display:table
一.star组件使用到了computed属性 computed相当于属性的一个实时计算,当对象的某个值改变的时候,会进行实时计算. computed: { starType() { return 's ...
- Easyui comboxgrid弹出窗增加搜索功能
效果如上图所示 <input id="q_item" name="q_item" style="width:200px" toolba ...
- luogu P1486 [NOI2004]郁闷的出纳员
一万年以后终于调过了这题 这道题主要是维护一个有序的集合(吧),所以使用平衡树(我这里用\(Splay\)) 记录一个变量\(ff\)(雾),表示所有工资的变化量 对于\(I\)操作,如果初始工资大于 ...
- cetus系列~ 读写分离具体分析
一 简介:上一章我们讲了cetus的基本安装,这章继续分析cetus 二 分析 1 基本配置 1 开启主从延迟检测需在后端数据库创建库proxy_heart_beat和表tb_heartb ...
- django学习~forms
一 简介 今天咱们来聊聊 django强大的表单功能二 Froms作用 1 自动生成HTML表单元素 2 检查表单数据的合法性 3 如果验证错误,重新显示表单(数据不会重置) 4 数据 ...