代码地址
https://github.com/erlieStar/studyShiro
JSP标签
JSP页面添加
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
标签名称 | 标签条件(均是显示标签内容) |
---|---|
<shiro:authenticated> | 登录之后 |
<shiro:notAuthenticated> | 不在登录状态时 |
<shiro:guest> | 用户在没有RememberMe时 |
<shiro:user> | 用户在RememberMe时 |
<shiro:hasAnyRoles name=”abc,123” > | 在有abc或者123角色时 |
<shiro:hasRole name=”abc”> | 拥有角色abc |
<shiro:lacksRole name=”abc”> | 没有角色abc |
<shiro:hasPermission name=”abc”> | 拥有权限资源abc |
<shiro:lacksPermission name=”abc”> | 没有abc权限资源 |
<shiro:principal> | 显示用户身份名称 |
<shiro:principal property=”username”> | 显示用户身份中的属性值 |
Shiro中默认的过滤器
过滤器名称 | 描述 |
---|---|
anon | 可以匿名使用 |
authc | 需要认证(登陆)才能使用 |
logout | 注销登陆时,完成一定的功能,任何现有的session都会失效,而且任何身份都将会失去关联(在Web应用程序中,RememberMe cookie也将会被删除) |
前言
整个项目是在学了开涛大佬博客的基础上搭建起来的,只看到了前16章,因为后面的东西也用不到,我就不重复造轮子了,就梳理一下大佬在GitHub上的shiro-example-chapter16的代码,因为这是一个简单的项目,看完也就理解的差不多了
spring-config-shiro.xml
<!-- 基于Form表单的身份验证过滤器 -->
<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<!--获取表单中的username字段-->
<property name="usernameParam" value="username"/>
<!--获取表单中的password字段-->
<property name="passwordParam" value="password"/>
<property name="rememberMeParam" value="rememberMe"/>
<property name="loginUrl" value="/login"/>
</bean>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--设置登陆的url,用户没有登陆时跳转到这个url-->
<property name="loginUrl" value="/login"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="sysUser" value-ref="sysUserFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/login = authc
/logout = logout
/authenticated = authc
/** = user,sysUser
</value>
</property>
</bean>
个人理解的项目运行流程(如果有不正确的地方,欢迎指出),首先根据Shiro的Web过滤器,进入/login这个controller中
@Controller
public class LoginController {
@RequestMapping(value = "/login" )
public String showLoginForm(HttpServletRequest req, Model model) {
String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
String error = null;
if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
error = "用户名/密码错误";
} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
error = "用户名/密码错误";
} else if(exceptionClassName != null) {
error = "其他错误:" + exceptionClassName;
}
model.addAttribute("error", error);
return "login";
}
}
这个Controller看起来很奇怪,其实是基于Form表单的身份验证过滤器在起作用,它会获取用户输入的username和password看是否正确,如果正确跳转到上一个路径,否则调用HttpServletRequest中的这个方法
setAttribute("shiroLoginFailure",)
这个设置的是异常的类名,这样在LoginController中通过比较异常名就可以知道是什么原因导致登陆失败了。
如果登陆正确,项目跳到IndexController中的/路径,并且获取权限和菜单信息,跳转到首页,这样就能进行各种操作了。
写自己的Realm是一般继承AuthorizingRealm,重写其中2个方法即可
public class MyRealm extends AuthorizingRealm{
/**
* 授权,验证权限时调用
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
}
/**
* 认证,登陆时调用
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
}
}
大佬的项目重写了很多方法,是用来清空缓存的,大佬的程序有个Bug,登陆成功后点击后退键,进入登陆页面后,再次输入用户名和密码一直登陆不上去,因此在我的项目中就没用基于Form表单的身份验证过滤器,而是自己制定了一下登陆成功和失败跳转的页面
项目思路
资源(sys_resource)
名称 | 类型 | 描述 |
---|---|---|
id | bigint | 编号,主键 |
name | varchar | 资源名称 |
type | varchar | 资源类型 |
priority | int | 显示顺序 |
parent_id | bigint | 父编号 |
permission | varchar | 权限字符串 |
available | bool | 是否可用 |
组织机构(sys_organization)
名称 | 类型 | 描述 |
---|---|---|
id | bigint | 编号,主键 |
name | varchar | 组织机构名 |
priority | int | 显示顺序 |
parent_id | bigint | 父编号 |
available | bool | 是否可用 |
用户(sys_user)
名称 | 类型 | 描述 |
---|---|---|
id | bigint | 编号,主键 |
username | varchar | 用户名 |
password | varchar | 密码 |
salt | varchar | 盐 |
available | bool | 是否可用 |
角色(sys_role)
名称 | 类型 | 描述 |
---|---|---|
id | bigint | 编号,主键 |
rolename | varchar | 角色名 |
description | varchar | 角色描述 |
available | bool | 是否可用 |
用户角色表(sys_user_role)
名称 | 类型 | 描述 |
---|---|---|
id | bigint | 编号,主键 |
user_id | bigint | 用户ID |
role_id | bigint | 角色ID |
角色资源表(sys_role_resource)
名称 | 类型 | 描述 |
---|---|---|
id | bigint | 编号,主键 |
role_id | bigint | 角色ID |
resource_id | bigint | 资源ID |
我自己写的项目将用户和角色的关系,角色和资源的关系写到一张表中,方便维护,也去掉了很多大佬用的没能理解的东西。在dao层,UserDao处理sys_user这张表,UserRoleDao处理sys_user_role这张表,以此类推,service层也是这种思路。除了新建OrganEntity,ResourceEntity,RoleEntity,UserEntity这4个实体类,还建立了RoleVo,UserVo这2个展示层对象,因为类似如下页面用Vo对象修改和展示比较方便
参考博客
为什么要在密码里加盐
[1]https://libuchao.com/2013/07/05/password-salt
SQL注入和XSS
[1]http://www.cnblogs.com/rush/category/339662.html