web环境下使用shiro
在SSM中集成shiro,使用shiro完成系统登录功能,相当于完成系统中的登录功能以及登录检查拦截器的功能。如果有对基本原理不太明白,请去翻看上一篇博客。
- 在web.xml添加shiro过滤器代理的配置,该代理会从spring容器中找到shiroFilter并执行
<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>
- 抽取shiro.xml专门配置shiro相关,在mvc中引入此配置即可
主要配置什么:配置shiroFilter,配置securityManager安全管理器,配置我们自定义的realm(我们自定义的类可以使用@Component取代xml配置),为shiroFilter注入securityManager,为securityManager注入realm。此外,在shiroFilter中我们需要为不同类型的请求资源去定义合适的过滤器
- 常用过滤器
a. anon:org.apache.shiro.web.filter.authc.AnonymousFilter,匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤
b. authc:org.apache.shiro.web.filter.authc.FormAuthenticationFilter,表示需要认证(登录)才能使用
c. roles:org.apache.shiro.web.filter.authz.RolesAuthorizationFilter,角色授权拦截器,验证用户是否拥有资源角色;
d. perms:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter,权限授权拦截器,验证用户是否拥有资源权限
e. logout:org.apache.shiro.web.filter.authc.LogoutFilter,访问/logout.do进行登陆注销
补充shiroFilter中的其他属性配置:usernameParam:表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名(rememberMe); loginUrl:登录页面地
注意前端页面表单参数必须为username和password,如果不是,请按照补充的内容做出相应修改
完整配置
<!--Ioc-->
<context:component-scan base-package="cn.wolfcode.crm.shiro"/>
<!--shiro相关配置-->
<!--配置shiro过滤器,注意此bean的名称必须和web.xml中filter-name保持一致-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!--配置登陆页面-->
<property name="loginUrl" value="/login.html"></property>
<!--配置过滤规则-->
<property name="filterChainDefinitions">
<value>
<!--静态资源不需要验证-->
/js/**=anon
/images/**=anon
/css/**=anon
/bootstrap/**=anon
/jquery/**=anon
<!--访问/logout.do进行登陆注销-->
/logout.do=logout
<!--其余资源都需要认证(登录)-->
/**=authc
</value>
</property>
<!--注意上述对authc的过滤是FormAuthenticationFilter在做,观察代码可知
登陆成功(onLoginSuccess)他只会返回true,失败返回false
但是我们表单是用ajax的post方式提交,会接收json数据,但是默认的FormAuthenticationFilter无法满足
此处我们自定义filter实现我们想要的功能,继承与FormAuthenticationFilter
-->
<property name="filters">
<map>
<entry key="authc" value-ref="authcFilter"></entry>
</map>
</property>
</bean>
<!--配置安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
- 我们自定义MyRealm,主要功能是根据传来的token调用service方法到数据库查处安全数据源,然后到封装认证信息并返回
@Component("myRealm")
public class MyRealm extends AuthorizingRealm {
@Autowired
private IEmployeeService employeeService;
//权限认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从数据库中获取安全数据
Employee emp = employeeService.getEmployeeByUsername(token.getPrincipal());
System.out.println("emp:"+emp);
if(emp == null){
return null;
}
return new SimpleAuthenticationInfo(emp.getName(),emp.getPassword(),getName());
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
- 我们自定义MyAuthcFilter继承于FormAuthenticationFilter,为什么自定义?针对于realm返回的info,如果没有任何异常,会进入FormAuthenticationFilter的onSuccessLogin()方法,会返回true;返回info为null,进入onLoginFailed()方法返回false;但是,我们前端页面使用ajax提交表单,需要接受json返回数据,所以FormAuthenticationFilter不满足需要,我们自定义Filter继承于它,可以覆盖上面两个方法,返回成功或失败的json数据
JsonResult:简单封装返回的json数据,success标志有没有成功,message用于传递信息
@Data
public class JsonResult {
private boolean success = true; //标志是否成功
private String message; //返回的信息
//如果成功,直接new对象返回,不需要加message
//如果失败,写下边的方法
public void mark(String msg){
this.success = false;
this.message = msg;
}
}
MyAuthcFilter:继承与FormAuthenticationFilter
@Component("authcFilter")
public class MyAuthcFilter extends FormAuthenticationFilter {
//登陆成功,响应json
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
response.setContentType("text/json;charset=utf-8");
PrintWriter writer = response.getWriter();
JsonResult jsonResult = new JsonResult();
writer.write(JSON.toJSONString(jsonResult));
return false;
}
//登陆失败。,响应json
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
response.setContentType("text/json;charset=utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e1) {
e1.printStackTrace();
}
JsonResult jsonResult = new JsonResult();
jsonResult.mark("亲,您的账号密码输入有误,请重新输入!");
writer.write(JSON.toJSONString(jsonResult));
return false;
}
}
- 总结
ShiroFilter进行拦截->DelegatingSubject开始执行login方法->login方法调用SecurityManager.login->调用权限认证器->调用MyRealm进行认证,返回正确认证信息->DelegatingSubject的login方法执行结束->执行我们自定义定义的MyauthcFilter的方法,响应成功或失败的json数据