shiro教程(4)-shiro与项目集成开发

shiro与项目集成开发

这里我们主要以用户登录的例子来演示,先给出一个时序图:

点击打开链接(点击查看)

1.1 shiro与spring web项目整合

shiro与springweb项目整合在“基于url拦截实现的工程”基础上整合,基于url拦截实现的工程的技术架构是springmvc+mybatis,整合注意两点:

1、shiro与spring整合

2、加入shiro对web应用的支持

1.1.1 取消原springmvc认证和授权拦截器

去掉springmvc.xml中配置的LoginInterceptor和PermissionInterceptor拦截器。

1.1.2 加入shiro的jar包

 

1.1.3 web.xml添加shiro Filter

<!-- shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter -->

<filter>

<filter-name>shiroFilter</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

<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>

 

1.1.4 applicationContext-shiro.xml 

<!-- Shiro 的Web过滤器 -->

<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="/refuse.jsp" />

<!-- shiro拦截器配置 -->

<property name="filters">

<map>

<entry key="authc" value-ref="formAuthenticationFilter" />

</map>

</property>

<property name="filterChainDefinitions">

<value>

<!-- 必须通过身份认证方可访问,身份认 证的url必须和过虑器中指定的loginUrl一致 -->

/loginsubmit.action = authc

<!-- 退出拦截,请求logout.action执行退出操作 -->

/logout.action = logout

<!-- 无权访问页面 -->

/refuse.jsp = anon

<!-- roles[XX]表示有XX角色才可访问 -->

/item/list.action = roles[item],authc

/js/** anon

/images/** anon

/styles/** anon

<!-- user表示身份认证通过或通过记住我认证通过的可以访问 -->

/** = user

<!-- /**放在最下边,如果一个url有多个过虑器则多个过虑器中间用逗号分隔,如:/** = user,roles[admin] -->

 

</value>

</property>

</bean>

 

 

<!-- 安全管理器 -->

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<property name="realm" ref="userRealm" />

</bean>

 

<!-- 自定义 realm -->

<bean id="userRealm" class="com.sihai.ssm.realm.CustomRealm1">

</bean>

<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->

<bean id="formAuthenticationFilter"

class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">

<!-- 表单中账号的input名称 -->

<property name="usernameParam" value="usercode" />

<!-- 表单中密码的input名称 -->

<property name="passwordParam" value="password" />

<!-- <property name="rememberMeParam" value="rememberMe"/> -->

<!-- loginurl:用户登陆地址,此地址是可以http访问的url地址 -->

<property name="loginUrl" value="/loginsubmit.action" />

</bean>

securityManager:这个属性是必须的。

loginUrl:没有登录认证的用户请求将跳转到此地址,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。

unauthorizedUrl:没有权限默认跳转的页面。

1.1.5 使用shiro注解授权

在springmvc.xml中配置shiro注解支持,可在controller方法中使用shiro注解配置权限:

<!-- 开启aop,对类代理 -->

<aop:config proxy-target-class="true"></aop:config>

<!-- 开启shiro注解支持 -->

<bean

class="

org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">

<property name="securityManager" ref="securityManager" />

</bean>

修改Controller代码,在方法上添加授权注解,如下:

// 查询商品列表

@RequestMapping("/queryItem")

@RequiresPermissions("item:query")

public ModelAndView queryItem() throws Exception {

上边代码@RequiresPermissions("item:query")表示必须拥有“item:query”权限方可执行。

其它的方法参考示例添加注解

1.1.6 自定义realm

此realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。

public class CustomRealm1 extends AuthorizingRealm {

 

@Autowired

private SysService sysService;

 

@Override

public String getName() {

return "customRealm";

}

 

// 支持什么类型的token

@Override

public boolean supports(AuthenticationToken token) {

return token instanceof UsernamePasswordToken;

}

 

// 认证

@Override

protected AuthenticationInfo doGetAuthenticationInfo(

AuthenticationToken token) throws AuthenticationException {

 

// 从token中 获取用户身份信息

String username = (String) token.getPrincipal();

// 拿username从数据库中查询

// ....

// 如果查询不到则返回null

if (!username.equals("zhang")) {// 这里模拟查询不到

return null;

}

 

// 获取从数据库查询出来的用户密码

String password = "123";// 这里使用静态数据模拟。。

// 根据用户id从数据库取出菜单

//...先用静态数据

List<SysPermission> menus = new ArrayList<SysPermission>();;

SysPermission sysPermission_1 = new SysPermission();

sysPermission_1.setName("商品管理");

sysPermission_1.setUrl("/item/queryItem.action");

SysPermission sysPermission_2 = new SysPermission();

sysPermission_2.setName("用户管理");

sysPermission_2.setUrl("/user/query.action");

menus.add(sysPermission_1);

menus.add(sysPermission_2);

// 构建用户身体份信息

ActiveUser activeUser = new ActiveUser();

activeUser.setUserid(username);

activeUser.setUsername(username);

activeUser.setUsercode(username);

activeUser.setMenus(menus);

 

// 返回认证信息由父类AuthenticatingRealm进行认证

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(

activeUser, password, getName());

 

return simpleAuthenticationInfo;

}

 

// 授权

@Override

protected AuthorizationInfo doGetAuthorizationInfo(

PrincipalCollection principals) {

// 获取身份信息

ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();

//用户id

String userid = activeUser.getUserid();

// 根据用户id从数据库中查询权限数据

// ....这里使用静态数据模拟

List<String> permissions = new ArrayList<String>();

permissions.add("item:query");

permissions.add("item:update");

 

// 将权限信息封闭为AuthorizationInfo

 

SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

for (String permission : permissions) {

simpleAuthorizationInfo.addStringPermission(permission);

}

 

return simpleAuthorizationInfo;

}

 

}

 

1.1.7 登录

//用户登陆页面

@RequestMapping("/login")

public String login()throws Exception{

return "login";

}

// 用户登陆提交

@RequestMapping("/loginsubmit")

public String loginsubmit(Model model, HttpServletRequest request)

throws Exception {

 

// shiro在认证过程中出现错误后将异常类路径通过request返回

String exceptionClassName = (String) request

.getAttribute("shiroLoginFailure");

if (UnknownAccountException.class.getName().equals(exceptionClassName)) {

throw new CustomException("账号不存在");

} else if (IncorrectCredentialsException.class.getName().equals(

exceptionClassName)) {

throw new CustomException("用户名/密码错误");

} else{

throw new Exception();//最终在异常处理器生成未知错误

}

}

1.1.8 首页

由于session由shiro管理,需要修改首页的controller方法:

//系统首页

@RequestMapping("/first")

public String first(Model model)throws Exception{

//主体

Subject subject = SecurityUtils.getSubject();

//身份

ActiveUser activeUser = (ActiveUser) subject.getPrincipal();

model.addAttribute("activeUser", activeUser);

return "/first";

}

1.1.9 退出

由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可。

<!-- 退出拦截,请求logout.action执行退出操作 -->

/logout.action = logout

1.1.10 无权限refuse.jsp

当用户无操作权限,shiro将跳转到refuse.jsp页面。

参考:applicationContext-shiro.xml

1.2 realm连接数据库

1.2.1 添加凭证匹配器

添加凭证匹配器实现md5加密校验。

修改applicationContext-shiro.xml:

<!-- 凭证匹配器 -->

<bean id="credentialsMatcher"

class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">

<property name="hashAlgorithmName" value="md5" />

<property name="hashIterations" value="1" />

</bean>

 

<!-- 自定义 realm -->

<bean id="userRealm" class="com.sihai.ssm.realm.CustomRealm1">

<property name="credentialsMatcher" ref="credentialsMatcher" />

</bean>

 

1.2.2 realm代码

修改realm代码从数据库中查询用户身份信息和权限信息,将sysService注入realm。

public class CustomRealm1 extends AuthorizingRealm {

 

@Autowired

private SysService sysService;

 

@Override

public String getName() {

return "customRealm";

}

 

// 支持什么类型的token

@Override

public boolean supports(AuthenticationToken token) {

return token instanceof UsernamePasswordToken;

}

 

@Override

protected AuthenticationInfo doGetAuthenticationInfo(

AuthenticationToken token) throws AuthenticationException {

// 从token中获取用户身份

String usercode = (String) token.getPrincipal();

 

SysUser sysUser = null;

try {

sysUser = sysService.findSysuserByUsercode(usercode);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

 

// 如果账号不存在

if (sysUser == null) {

throw new UnknownAccountException("账号找不到");

}

 

// 根据用户id取出菜单

List<SysPermission> menus = null;

try {

menus = sysService.findMenuList(sysUser.getId());

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

// 用户密码

String password = sysUser.getPassword();

//盐

String salt = sysUser.getSalt();

// 构建用户身体份信息

ActiveUser activeUser = new ActiveUser();

activeUser.setUserid(sysUser.getId());

activeUser.setUsername(sysUser.getUsername());

activeUser.setUsercode(sysUser.getUsercode());

activeUser.setMenus(menus);

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(

activeUser, password, ByteSource.Util.bytes(salt),getName());

return simpleAuthenticationInfo;

}

 

@Override

protected AuthorizationInfo doGetAuthorizationInfo(

PrincipalCollection principals) {

//身份信息

ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();

//用户id

String userid = activeUser.getUserid();

//获取用户权限

List<SysPermission> permissions = null;

try {

permissions = sysService.findSysPermissionList(userid);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//构建shiro授权信息

SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

for(SysPermission sysPermission:permissions){

simpleAuthorizationInfo.addStringPermission(sysPermission.getPercode());

}

return simpleAuthorizationInfo;

}

 

}

1.3 缓存

shiro每个授权都会通过realm获取权限信息,为了提高访问速度需要添加缓存,第一次从realm中读取权限数据,之后不再读取,这里Shiro和Ehcache整合。

1.3.1 添加Ehcache的jar包

1.3.2 配置

在applicationContext-shiro.xml中配置缓存管理器。

<!-- 安全管理器 -->

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<property name="realm" ref="userRealm" />

<property name="sessionManager" ref="sessionManager" />

<property name="cacheManager" ref="cacheManager"/>

</bean>

 

<!-- 缓存管理器 -->

    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">

    </bean>

1.4 session管理

在applicationContext-shiro.xml中配置sessionManager:

<!-- 安全管理器 -->

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<property name="realm" ref="userRealm" />

<property name="sessionManager" ref="sessionManager" />

</bean>

<!-- 会话管理器 -->

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">

        <!-- session的失效时长,单位毫秒 -->

        <property name="globalSessionTimeout" value="600000"/>

        <!-- 删除失效的session -->

        <property name="deleteInvalidSessions" value="true"/>

    </bean>

1.5 验证码

1.5.1 自定义FormAuthenticationFilter

需要在验证账号和名称之前校验验证码。

public class MyFormAuthenticationFilter extends FormAuthenticationFilter {

protected boolean onAccessDenied(ServletRequest request,

ServletResponse response, Object mappedValue) throws Exception {

 

// 校验验证码

// 从session获取正确的验证码

HttpSession session = ((HttpServletRequest)request).getSession();

//页面输入的验证码

String randomcode = request.getParameter("randomcode");

//从session中取出验证码

String validateCode = (String) session.getAttribute("validateCode");

if (!randomcode.equals(validateCode)) {

// randomCodeError表示验证码错误

request.setAttribute("shiroLoginFailure", "randomCodeError");

//拒绝访问,不再校验账号和密码

return true;

}

return super.onAccessDenied(request, response, mappedValue);

}

}

1.5.2 修改FormAuthenticationFilter配置

修改applicationContext-shiro.xml中对FormAuthenticationFilter的配置。

<bean id="formAuthenticationFilter"

class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">

 

改为

<bean id="formAuthenticationFilter"

class="com.sihai.ssm.shiro.MyFormAuthenticationFilter">

 

1.5.3 登陆页面

添加验证码:

<TR>

<TD>验证码:</TD>

<TD><input id="randomcode" name="randomcode" size="8" /> <img

id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""

width="56" height="20" align='absMiddle' /> <a

href=javascript:randomcode_refresh()>刷新</a></TD>

</TR>

1.5.4 配置validatecode.jsp匿名访问

修改applicationContext-shiro.xml:

1.6 记住我

用户登陆选择“自动登陆”本次登陆成功会向cookie写身份信息,下次登陆从cookie中取出身份信息实现自动登陆。

1.6.1 用户身份实现java.io.Serializable接口

向cookie记录身份信息需要用户身份信息对象实现序列化接口,如下:

 

1.6.2 配置

<!-- 安全管理器 -->

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<property name="realm" ref="userRealm" />

<property name="sessionManager" ref="sessionManager" />

<property name="cacheManager" ref="cacheManager"/>

<!-- 记住我 -->

<property name="rememberMeManager" ref="rememberMeManager"/>

</bean>

 

<!-- rememberMeManager管理器 -->

<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">

<property name="cookie" ref="rememberMeCookie" />

</bean>

<!-- 记住我cookie -->

<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">

<constructor-arg value="rememberMe" />

<!-- 记住我cookie生效时间30天 -->

<property name="maxAge" value="2592000" />

</bean>

 

修改formAuthenticationFitler添加页面中“记住我checkbox”的input名称:

<bean id="formAuthenticationFilter"

class="com.sihai.ssm.shiro.MyFormAuthenticationFilter">

<!-- 表单中账号的input名称 -->

<property name="usernameParam" value="usercode" />

<!-- 表单中密码的input名称 -->

<property name="passwordParam" value="password" />

<property name="rememberMeParam" value="rememberMe"/>

<!-- loginurl:用户登陆地址,此地址是可以http访问的url地址 -->

<property name="loginUrl" value="/loginsubmit.action" />

</bean>

1.6.3 登陆页面

在login.jsp中添加“记住我”checkbox。

<TR>

<TD></TD>

<TD>

<input type="checkbox" name="rememberMe" />自动登陆

</TD>

</TR>

附:

2.1 shiro过虑器

过滤器简称

对应的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表示需要认证(登录)才能使用,没有参数

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是授权过滤器

2.2 shiro的jsp标签

Jsp页面添加:

<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>

标签名称

标签条件(均是显示标签内容)

<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"/>     显示用户身份中的属性值

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello-java-maker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值