shiro整合SpringMVC
来源:互联网 发布:pid算法实例c语言 编辑:程序博客网 时间:2024/06/11 18:21
一、前言
前面我们主要讲解了写什么,讲解通过ini文件加载,自定义realm源(ishi加密),对用户认证和权限授权。这一章开始,我们开始整合shrio到web项目中,当然还是与我们之前的springmvc与mybatis整合。这里的mybatis不强求,如果你整合的时候也可以用hibernate,只不过是另一个数据框架。
二、整合spring
2.1 基础框架
语言准备好基础框架:springmvc+mybatis的整合文件,项目采用maven
2.2jar包导入,pom.xml引入
<!--======================================shrio=================================================-->
<
dependency
>
<
groupId
>org.apache.shiro</
groupId
>
<
artifactId
>shiro-core</
artifactId
>
<
version
>1.2.4</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.apache.shiro</
groupId
>
<
artifactId
>shiro-web</
artifactId
>
<
version
>1.2.4</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.apache.shiro</
groupId
>
<
artifactId
>shiro-spring</
artifactId
>
<
version
>1.2.4</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.apache.shiro</
groupId
>
<
artifactId
>shiro-ehcache</
artifactId
>
<
version
>1.2.4</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.apache.shiro</
groupId
>
<
artifactId
>shiro-quartz</
artifactId
>
<
version
>1.2.4</
version
>
</
dependency
>
2.3在web.xml配置filter
<!--===============================================shiro过虑器================================================-->
<!-- shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter代理模式(律师模式) -->
<
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
>
2.4在spring中配置filter
配置完毕web.xml,我们需要在spring设置我们filter,主要配置:(拒绝权限地址,用户认证跳转页面)1、过滤器,2、安全管理器, 3、自己定义realm
跟我们用ini配置是思想是一样的。拦截---交给shrio的filter---shrio拦截
(applicationContext-shiro.xml)
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc
=
"http://www.springframework.org/schema/mvc"
xmlns:context
=
"http://www.springframework.org/schema/context"
xmlns:aop
=
"http://www.springframework.org/schema/aop"
xmlns:tx
=
"http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd ">
<!-- Shiro 的Web过滤器 -->
<!--1、与web.xml对应的bean-->
<
bean
id
=
"shiroFilter"
class
=
"org.apache.shiro.spring.web.ShiroFilterFactoryBean"
>
<
property
name
=
"securityManager"
ref
=
"securityManager"
/>
<!-- 如果没有认证将要跳转的登陆地址,http可访问的url,如果不在表单认证过虑器FormAuthenticationFilter中指定此地址就为身份认证地址 -->
<
property
name
=
"loginUrl"
value
=
"/login.action"
/>
<!-- 没有权限跳转的地址 -->
<
property
name
=
"unauthorizedUrl"
value
=
"/pages/jsp/refuse.jsp"
/>
<!--过滤定义,从上而下,蒋匿名的anon放最下面-->
<
property
name
=
"filterChainDefinitions"
>
<
value
>
/** anon
</
value
>
</
property
>
</
bean
>
<!-- 2、安全管理器 -->
<
bean
id
=
"securityManager"
class
=
"org.apache.shiro.web.mgt.DefaultWebSecurityManager"
>
<
property
name
=
"realm"
ref
=
"userRealm"
/>
</
bean
>
<!--3、realm-->
<
bean
id
=
"userRealm"
class
=
"com.ycy.shiro.CustomRealm"
/>
</
beans
>
2.4.1 shrio过滤器
上面所说的过滤器需要配置文件,配置如下
过滤器简称
对应的Java类
anon
org.apache.shiro.web.filter.authc.AnonymousFilter
authc
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port
org.apache.shiro.web.filter.authz.PortFilter
rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl
org.apache.shiro.web.filter.authz.SslFilter
user
org.apache.shiro.web.filter.authc.UserFilter
logout
org.apache.shiro.web.filter.authc.LogoutFilter
Shiro-1.2.2内置的FilterChain
=========================================================================================================================
1)Shiro验证URL时,URL匹配成功便不再继续匹配查找(所以要注意配置文件中的URL顺序,尤其在使用通配符时)
故filterChainDefinitions的配置顺序为自上而下,以最上面的为准
2)当运行一个Web应用程序时,Shiro将会创建一些有用的默认Filter实例,并自动地在[main]项中将它们置为可用
自动地可用的默认的Filter实例是被DefaultFilter枚举类定义的,枚举的名称字段就是可供配置的名称
anon---------------org.apache.shiro.web.filter.authc.AnonymousFilter
authc--------------org.apache.shiro.web.filter.authc.FormAuthenticationFilter
perms--------------org.apache.shiro.web.filter.authz.PermissionAuthorizationFilter
port---------------org.apache.shiro.web.filter.authz.PortFilter
rest---------------org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles--------------org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl----------------org.apache.shiro.web.filter.authz.SslFilter
user---------------org.apache.shiro.web.filter.authz.UserFilter
=========================================================================================================================
3)通常可将这些过滤器分为两组
anon,authc,authcBasic,user是第一组认证过滤器
perms,port,rest,roles,ssl是第二组授权过滤器
注意user和authc不同:当应用开启了rememberMe时,用户下次访问时可以是一个user,但绝不会是authc,因为authc是需要重新认证的
user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
说白了,以前的一个用户登录时开启了rememberMe,然后他关闭浏览器,下次再访问时他就是一个user,而不会authc
=========================================================================================================================
4)举几个例子
/admin=authc,roles[admin] 表示用户必需已通过认证,并拥有admin角色才可以正常发起'/admin'请求
/edit=authc,perms[admin:edit] 表示用户必需已通过认证,并拥有admin:edit权限才可以正常发起'/edit'请求
/home=user 表示用户不一定需要已经通过认证,只需要曾经被Shiro记住过登录状态就可以正常发起'/home'请求
=========================================================================================================================
5)各默认过滤器常用如下(注意URL Pattern里用到的是两颗星,这样才能实现任意层次的全匹配)
/admins/**=anon 无参,表示可匿名使用,可以理解为匿名用户或游客
/admins/user/**=authc 无参,表示需认证才能使用
/admins/user/**=authcBasic 无参,表示httpBasic认证
/admins/user/**=user 无参,表示必须存在用户,当登入操作时不做检查
/admins/user/**=ssl 无参,表示安全的URL请求,协议为https
/admins/user/**=perms[user:add:*]
参数可写多个,多参时必须加上引号,且参数之间用逗号分割,如/admins/user/**=perms["user:add:*,user:modify:*"]
当有多个参数时必须每个参数都通过才算通过,相当于isPermitedAll()方法
/admins/user/**=port[8081]
当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString
其中schmal是协议http或https等,serverName是你访问的Host,8081是Port端口,queryString是你访问的URL里的?后面的参数
/admins/user/**=rest[user]
根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等
/admins/user/**=roles[admin]
参数可写多个,多个时必须加上引号,且参数之间用逗号分割,如/admins/user/**=roles["admin,guest"]
当有多个参数时必须每个参数都通过才算通过,相当于hasAllRoles()方法
2.5自定义realm的编写
realm从数据库查询用户信息,将用户菜单、usercode、username等设置在SimpleAuthenticationInfo中。数据库表可参考前面几个章节的mapper.xml
package
com.ycy.shiro;
import
java.util.ArrayList;
import
java.util.List;
import
com.ycy.model.ActiveUser;
import
com.ycy.model.SysPermission;
import
com.ycy.model.SysUser;
import
com.ycy.service.SysService;
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.SimpleAuthenticationInfo;
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.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Component;
import
org.springframework.stereotype.Service;
/**
*
* <p>
* Title: CustomRealm
* </p>
* <p>
* Description:自定义realm
* </p>
*/
public
class
CustomRealm
extends
AuthorizingRealm {
//注入service
@Autowired
private
SysService sysService;
// 设置realm的名称
@Override
public
void
setName(String name) {
super
.setName(
"customRealm"
);
}
// 用于认证
//realm的认证方法,从数据库查询用户信息
@Override
protected
AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token)
throws
AuthenticationException {
// token是用户输入的用户名和密码
// 第一步从token中取出用户名
String userCode = (String) token.getPrincipal();
// 第二步:根据用户输入的userCode从数据库查询
SysUser sysUser =
null
;
try
{
sysUser = sysService.findSysUserByUserCode(userCode);
}
catch
(Exception e1) {
e1.printStackTrace();
}
// 如果查询不到返回null
if
(sysUser==
null
){
//
return
null
;
}
// 从数据库查询到密码
String password = sysUser.getPassword();
//盐
String salt = sysUser.getSalt();
// 如果查询到返回认证信息AuthenticationInfo
//activeUser就是用户身份信息
ActiveUser activeUser =
new
ActiveUser();
activeUser.setUserid(sysUser.getId());
activeUser.setUsercode(sysUser.getUsercode());
activeUser.setUsername(sysUser.getUsername());
//..
//根据用户id取出菜单
List<SysPermission> menus =
null
;
try
{
//通过service取出菜单
menus = sysService.findMenuListByUserId(sysUser.getId());
}
catch
(Exception e) {
e.printStackTrace();
}
//将用户菜单 设置到activeUser
activeUser.setMenus(menus);
//将activeUser设置simpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo =
new
SimpleAuthenticationInfo(
activeUser, password,ByteSource.Util.bytes(salt),
this
.getName());
return
simpleAuthenticationInfo;
}
// 用于授权
@Override
protected
AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//从 principals获取主身份信息
//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//根据身份信息获取权限信息
//从数据库获取到权限数据
List<SysPermission> permissionList =
null
;
try
{
permissionList = sysService.findMenuListByUserId(activeUser.getUserid());
}
catch
(Exception e) {
e.printStackTrace();
}
//单独定一个集合对象
List<String> permissions =
new
ArrayList<String>();
if
(permissionList!=
null
){
for
(SysPermission sysPermission:permissionList){
//将数据库中的权限标签 符放入集合
permissions.add(sysPermission.getPercode());
}
}
//查到权限数据,返回授权信息(要包括 上边的permissions)
SimpleAuthorizationInfo simpleAuthorizationInfo =
new
SimpleAuthorizationInfo();
//将上边查询到授权信息填充到simpleAuthorizationInfo对象中
simpleAuthorizationInfo.addStringPermissions(permissions);
return
simpleAuthorizationInfo;
}
//清除缓存
public
void
clearCached() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super
.clearCache(principals);
}
}
注意:先前的我们springmvc整合项目时候,先加载审springmvc:scan扫描是配置在springmvc里面,这个需要重新配置到spring
<
param-value
>classpath:spring/applicationContext-*.xml</
param-value
>
配置
<
context:component-scan
base-package
=
"com.ycy"
/>
到这个applicationContext-*.xml中里面,因为这个是第一加载顺序。
三、shrio实现登录退出实例
上面已经实现了整合,说白了就是加一个shrio的拦截器而已。真正的实现还没做,我们以登录退出来做一个实例。
3.1登录
使用FormAuthenticationFilter过虑器实现 ,原理如下:
将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl
FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)
FormAuthenticationFilter调用realm传入一个token(username和password)
realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。
如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)
3.1.1 LoginController
package
com.ycy.controller;
import
com.ycy.Exception.CustomException;
import
com.ycy.model.ActiveUser;
import
com.ycy.service.SysService;
import
org.apache.shiro.authc.IncorrectCredentialsException;
import
org.apache.shiro.authc.UnknownAccountException;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpSession;
/**
*
* <p>Title: LoginController</p>
* <p>Description: 登陆和退出</p>
*/
@Controller
public
class
LoginController {
@Autowired
private
SysService sysService;
//用户登陆提交方法
/*@RequestMapping("/login")
public String login(HttpSession session,String usercode,String password,String randomcode)throws Exception{
//校验验证码
//从session获取正确的验证码
String validateCode = (String)session.getAttribute("validateCode");
if(!randomcode.equals(validateCode)){
//抛出异常:验证码错误
throw new CustomException("验证码 错误 !");
}
//用户身份认证
ActiveUser activeUser = sysService.authenticat(usercode, password);
//记录session
session.setAttribute("activeUser", activeUser);
//重定向到商品查询页面
return "redirect:/first";
}
//用户退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session失效
session.invalidate();
//重定向到商品查询页面
return "redirect:/items/queryItems";
}*/
@RequestMapping
(
"/login"
)
public
String login(HttpServletRequest request)
throws
Exception{
//如果登录失败从request中获取认证异常信息,shrioLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute(
"shiroLoginFailure"
);
//根据shrio返回的异常路径判断,抛出指定异常信息
if
(exceptionClassName!=
null
){
if
(UnknownAccountException.
class
.getName().equals(exceptionClassName)){
//抛出异常
throw
new
CustomException(
"账户不存在"
);
}
else
if
(IncorrectCredentialsException.
class
.getName().equals(exceptionClassName)){
throw
new
CustomException(
"用户名/密码错误"
);
}
else
{
throw
new
Exception(
"未知错误"
);
}
}
return
"login"
;
}
}
3.1.2 配置applicationContext-shiro.xml
package
com.ycy.controller;
import
com.ycy.Exception.CustomException;
import
com.ycy.model.ActiveUser;
import
com.ycy.service.SysService;
import
org.apache.shiro.authc.IncorrectCredentialsException;
import
org.apache.shiro.authc.UnknownAccountException;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpSession;
/**
*
* <p>Title: LoginController</p>
* <p>Description: 登陆和退出</p>
*/
@Controller
public
class
LoginController {
@Autowired
private
SysService sysService;
//用户登陆提交方法
/*@RequestMapping("/login")
public String login(HttpSession session,String usercode,String password,String randomcode)throws Exception{
//校验验证码
//从session获取正确的验证码
String validateCode = (String)session.getAttribute("validateCode");
if(!randomcode.equals(validateCode)){
//抛出异常:验证码错误
throw new CustomException("验证码 错误 !");
}
//用户身份认证
ActiveUser activeUser = sysService.authenticat(usercode, password);
//记录session
session.setAttribute("activeUser", activeUser);
//重定向到商品查询页面
return "redirect:/first";
}
//用户退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session失效
session.invalidate();
//重定向到商品查询页面
return "redirect:/items/queryItems";
}*/
@RequestMapping
(
"/login"
)
public
String login(HttpServletRequest request)
throws
Exception{
//如果登录失败从request中获取认证异常信息,shrioLoginFailure就是shiro异常类的全限定名
String exceptionClassName=(String)request.getAttribute(
"shiroLoginFailure"
);
//根据shrio返回的异常路径判断,抛出指定异常信息
if
(exceptionClassName!=
null
){
if
(UnknownAccountException.
class
.getName().equals(exceptionClassName)){
//抛出异常
throw
new
CustomException(
"账户不存在"
);
}
}
else
if
(IncorrectCredentialsException.
class
.getName().equals(exceptionClassName)){
throw
new
CustomException(
"用户名/密码错误"
);
}
return
"login"
;
}
}
3.1.3 加入首页数据
//系统首页
@RequestMapping
(
"/first"
)
public
String first(Model model)
throws
Exception{
//从shiro的session中取出activeUser
Subject subject= SecurityUtils.getSubject();
ActiveUser activeUser=(ActiveUser)subject.getPrincipal();
//通过model传送到页面
model.addAttribute(
"activeUser"
,activeUser);
return
"first"
;
}
3.2 退出
退出我们已经用shiro的。很简单
<!--请求logout,shrio擦除sssion-->
/logout=logout
展示页面:
3.3 总结登录退出实例5步走
1、web.xml配置shiroFileter拦截器
2、applicationContext-shiro配置:1、首先配置 shiroFileter 2、设置securityManager 3、设置过滤表达式 4、设置我们自己的realm
3、再配置loginController,根据异常返回用户认证错误信息
4、配置FristController,从securityUtil.getInstance(),加入环境。然后获取subject主体,再用属性注入到实体
5、在frist.jsp 获取controller传入的activeUser数据展示。
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- springmvc整合shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- Springmvc整合shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- SpringMVC整合Shiro
- Go语言学习笔记(二)基本数据
- python: tcp/ip学习、端口号、UDP的发送和接受数据、绑定端口号、模拟聊天室
- 图像识别初识
- HDU1874-畅通工程续-最短路
- 欢迎使用CSDN-markdown编辑器
- shiro整合SpringMVC
- minetest源码解析四:game界面刷新绘制流程及核心函数介绍
- Flask实战1-轻博客
- HPU1409 Watch Dog(Prim,最小生成树,河南省多校连萌(三))
- 2017 暑假艾教集训 day11 线段树!
- MySQL之锁
- 在linux系统下编程,解决scanf不能输入空格的问题
- shiro的注解和缓存
- [题解]bzoj1086 SCOI2005王室联邦