Shiro使用总结
背景:本人刚刚把Shiro整合在了SSM架构的项目上,maven做项目管理工具,实现了简单的认证和授权,以及记住我和验证码功能,简单的记录一下;
需要ssm项目demo的可以看我之前的文章:最简单易懂的Idea搭建SSM项目过程和配置(文末有demo哦)
寄语:代码改变世界不敢说,活在当下,敲好每一行代码就可以了
一、引入Shiro依赖
我用的Shiro版本是1.2.5,刚开始是引入shiro-all的,后来看到官网上强烈建议不要引入shiro-all,而是要按需引入,我觉得相当有道理哇,于是就引入了下面三个依赖。
<properties>
<shiro.version>1.2.5</shiro.version>
</properties>
<!-- shiro -->
<!-- shiro核心包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 添加shiro web支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 添加shiro spring整合 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
二、在web.xml中添加Shiro过滤器
此处过滤所有请求,所以filter-mapping中url-pattern设置的是/*
<!--shiro配置-->
<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>
三、配置sping-shiro.xml文件
在spring的配置文件中引入spring-shiro.xml
<!--引入spring-shiro-->
<import resource="spring-shiro.xml"/>
Shiro配置说明
Apache Shiro的配置主要分为四部分:
- 对象和属性的定义与配置
- URL的过滤器配置
- 静态用户配置
- 静态角色配置
配置Shiro对象
主要是为了自定义各个Shiro组件,项目比较简单,我只自定义了Relam和记住我功能
<!--创建shiro的安全管理器的对象-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myShiroRealm" />
<!--声明会话管理器属性-->
<!--<property name="sessionManager" ref="sessionManager"></property>-->
<!--声明rememberMe-->
<property name="rememberMeManager" ref="rememberMeManager"></property>
</bean>
自定义Relam和记住我
<!-- 项目自定义的Realm -->
<bean id="myShiroRealm" class="com.blueice.shiro.MyShiroRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
</bean>
<!--记住我的配置-->
<!--声明cookie对象-->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"></constructor-arg>
<!--只有http的链接才能使用cookie-->
<property name="httpOnly" value="true"></property>
<!--cookie的失效时间30天,单位是秒-->
<property name="maxAge" value="2592000"></property>
</bean>
<!--声明记住我的管理器对象-->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="rememberMeCookie"></property>
</bean>
配置Shiro过滤器
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/welcome" />
<property name="unauthorizedUrl" value="/login" />
<property name="filters">
<util:map>
<entry key="authc" value-ref="captchaFormAuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/static/** = anon
/plugins/** = anon
/kaptcha = anon
/kaptcha/get= anon
/login* = anon
/logout = logout
/** = authc
/** = user
</value>
</property>
</bean>
本人项目中引入了谷歌开源框架Kaptcha做验证码校验,因此自定义了一个authc的过滤器captchaFormAuthenticationFilter,重写了创建token的方式,加入了验证码字段。
<!--自定义添加验证码的过滤器-->
<bean id="captchaFormAuthenticationFilter" class="com.blueice.filter.MyFormAuthenticationFilter"/>
URL表达式说明
1、URL目录是基于HttpServletRequest.getContextPath()此目录设置
2、URL可使用通配符,**代表任意子目录
3、Shiro验证URL时,URL匹配成功便不再继续匹配查找。所以要注意配置文件中的URL顺序,尤其在使用通配符时。
Filter Chain定义说明
1、一个URL可以配置多个Filter,使用逗号分隔
2、当设置多个过滤器时,全部验证通过,才视为通过
3、部分过滤器可指定参数,如perms,roles
Shiro内置的FilterChain
Filter Name | 功能 |
---|---|
anno | 不需要授权、登录就可以访问。eg:/login |
authc | **需要登录授权才能访问。 |
authcBasic | Basic HTTP身份验证拦截器 |
logout | 退出拦截器。退出成功后,会 redirect到设置的/URI |
noSessionCreation | 不创建会话连接器 |
perms | 授权拦截器:perm['user:create'] |
port | 端口拦截器.eg:port[80] |
rest | rest风格拦截器 |
roles | 角色拦截器。eg:role[administrator] |
ssl | ssl拦截器。通过https协议才能通过 |
user | 用户拦截器。eg:登录后(authc),第二次没登陆但是有记住我(remember)都可以访问。 |
四、登录认证
收集token信息
/*先把账号密码传入shiro里面的UsernamePasswordToken对象里面*/
CaptchaToken token = new CaptchaToken(username, pwd, captchaCode, rememberMe);
提交token信息并处理
/*创建Subject对象*/
Subject subject = SecurityUtils.getSubject();
/*调用subject.login()进行登录*/
try {
subject.login(token);
} catch (UnknownAccountException e) {
error = "用户名/密码错误!";
} catch (IncorrectCredentialsException e) {
error = "用户名/密码错误!";
} catch (ExcessiveAttemptsException e) {
error = "登录失败多次,账户锁定10分钟!";
} catch (CaptchaException e) {
error = "验证码输入有误!";
} catch (AuthenticationException e) {
/*其他错误,比如锁定,如果想单独处理请单独catch处理*/
System.out.println(e.getMessage());
error = "未知异常,请联系管理员处理!";
}
五、注销用户
logout()会删除session中的所有信息,完成用户的注销操作。
Session session = SecurityUtils.getSubject().getSession();
Subject subject = SecurityUtils.getSubject();
subject.logout();
六、授权实现
本文中通过注解 @RequiresPermissions实现授权
@RequestMapping("/test")
@ResponseBody
@RequiresPermissions("user:role:tes")
public String testMap() {
Map<String, String> map = indexService.testMap();
return map.toString();
}
七、自定义Relam
Relam是Shiro的三大核心(Subject、SecurityManager、Relam)之一,通过集成AuthorizingRelam实现自定义Relam,重写了授权信息的doGetAuthorizationInfo方法和认证信息的doGetAuthenticationInfo方法。
public class MyShiroRealm extends AuthorizingRealm {
}
认证实现
Relam会根据上文login()方法提供的token进行认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
SysUser user = null;
if (username != null && !"".equals(username)) {
user = indexService.selectUserInfoByUsername(username);
}
if (null == user) {
/*没找到帐号*/
throw new UnknownAccountException("用户信息不存在!");
}
if (user.getStatus() == 0) {
/*用户被停用*/
throw new LockedAccountException("用户信息已被停用");
}
ByteSource salt = ByteSource.Util.bytes(user.getUsername());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), user.getPassword(),
salt, getName());
return info;
}
授权实现
获取数据库中当前登录用户的权限列表,跟@RequiresPermissions的进行比对,看用户是否有此授权,此处为了方便起见,直接设置了权限为"user:role:test",更多权限一般用逗号分隔。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String)principals.getPrimaryPrincipal();
/*用户权限列表*/
Set<String> permsSet = new HashSet<>();
permsSet.add("user:role:test");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setStringPermissions(permsSet);
return authorizationInfo;
}
结语
我是通过Md2All进行排版的,但是代码中//注释会读不到换行,导致后面全部被当成注释,所以我换成了/**/注释;
Md2All中的代码样式,如果是黑色背景,到了博客园都会变得很难看,最后我选了个白色背景的!
以上是我此次Shiro使用的总结,互相学习,多多交流哈!