本文中使用的项目架构是springMVC+mybatis,所以我们是基于搭建好的项目进行改造的。
sql脚步放到项目中,项目上传到共享的资源中,文章最后给出共享url。
除了前面文章提到的shiro-core相关的jar包,还需要如下:
在web系统中,shiro 也是通过filter进行拦截的。filter拦截后将操作交给filterChain(过滤器炼)。shiro中提供了多个filter,在栏目 shiro的过滤器 会全部介绍
在web中配置filter,如下:
<filter>
<filter-name>shiroFilterfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
<init-param>
<param-name>targetFilterLifecycleparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>targetBeanNameparam-name>
<param-value>shiroFilterparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>shiroFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
内容下图:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.action" />
<property name="successUrl" value="/first.action" />
<property name="unauthorizedUrl" value="/refuse.jsp" />
<property name="filterChainDefinitions">
<value>
/images/** = anon
/js/** = anon
/styles/** = anon
/validatecode.jsp = anon
/** = anon
value>
property>
bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
bean>
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm"/>
上面配置的CustomRealm
的内容和文章shiro权限框架详解05-shiro授权的CustomRealm一样。
在界面输入CustomRealm
代码中的账号为 zhangsan 密码为 123 可以进入欢迎页面。
但是并没有菜单和相关用户信息
由于登录使用的是 org.apache.shiro.web.filter.authc.FormAuthenticationFilter
filter实现的,具体流程如下:
如果用户没有认证时,请求上面配置的loginUrl进行认证,用户的身份信息和密码提交到loginUrl,FormAuthenticationFilter拦截取出request中的username和password(参数的key是可以进行配置的,下一篇blog介绍)参数值。FormAuthenticationFilter调用realm传入一个token(包含username和password),realm根据username查询用户信息,如果查询不到,返回null,FormAuthenticationFilter向request域中填充一个参数,key为shiroLoginFailure
记录异常信息。如果不为空的话,返回AuthenticationInfo
类。
由于FormAuthenticationFilter的身份信息和密码的请求参数的key默认是(username和password),修改login.jsp页面的账号和密码输入框name属性值,并注释掉验证码的代码。
修改LoginController
的 login
方法如下:
@RequestMapping("/login")
public String login(HttpServletRequest request)throws Exception{
//如果登录失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
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();//最终在异常处理器生成未知错误
}
}
//此方法不处理登录成功(认证成功),shiro认证成功会自动跳转到上一个请求路径。
//登录失败还到login页面
return "login";
}
这个方法只有校验不通过的时候才会执行。真正的校验方法是在自定义的realm中,校验身份是否正确。
将所有请求都改为需要认证才能访问的。
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.action" />
<property name="successUrl" value="/first.action" />
<property name="unauthorizedUrl" value="/refuse.jsp" />
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
map>
property>
<property name="filterChainDefinitions">
<value>
/images/** = anon
/js/** = anon
/styles/** = anon
/validatecode.jsp = anon
/** = authc
value>
property>
bean>
在登录界面输入账号为 zhangsan 密码为 123 验证是否登录成功,然后输入错误的账号,控制台出现下面的异常信息:
如果输入密码错误:
异常信息和前面Java项目演示的一样。
退出不需要我们自己实现,只要去访问一个退出的url(该url是可以不存在的)即可,由LogoutFilter filter处理,清除session。在applicationContext.xml配置logoutFilter
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.action" />
<property name="successUrl" value="/first.action" />
<property name="unauthorizedUrl" value="/refuse.jsp" />
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
map>
property>
<property name="filterChainDefinitions">
<value>
/images/** = anon
/js/** = anon
/styles/** = anon
/validatecode.jsp = anon
/logout.action = logout
/** = authc
value>
property>
bean>
删除原来的退出方法。
/**
* 用于认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//第一步:通过token获取身份信息
String userCode = (String) token.getPrincipal();
if (!"zhangsan".equals(userCode)) {
// 这里模仿查询不到
return null;
}
String password = "123";
//模拟数据activeUser就是用户身份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid("zhangsan");
activeUser.setUsercode("zhangsan");
activeUser.setUsername("张三");
//查询菜单信息
List menus = null;
try {
menus = sysService.findMenuListByUserId(activeUser .getUsercode());
} catch (Exception e) {
e.printStackTrace();
}
activeUser.setMenus(menus);
//如果查询到结果返回AuthenticationInfo
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(activeUser, password, "");
return authenticationInfo;
}
上面的代码通过模拟从数据库获取数据设置到ActiveUser中。
修改代码将获取身份信息,并放入request域中用于页面显示。
//系统首页
@RequestMapping("/first")
public String first(Model model)throws Exception{
Subject subject =SecurityUtils.getSubject();
ActiveUser activeUser = (ActiveUser)subject.getPrincipal();
model.addAttribute("activeUser", activeUser);
return "/first";
}
在登录界面,账号输入 zhangsan , 密码输入 123
如果成功的话,将会看到如下页面。
过滤器简称 | 对应的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 |
各个过滤器的介绍和作用:
anon:例子/admins/**=anon
没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc
表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数 。
roles:例子 /admins/user/** =roles[admin]
,参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/** =roles["admin,guest"]
,每个参数通过才算通过,相当于hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*]
,参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"]
,当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user]
,根据请求的方法,相当于/admins/user/**=perms[user:method]
,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081]
,当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString
,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic
没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查
注:
anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器
点击进入下载页面