一般没有登录也可以查看各个板块
1.登录之后才能查看(拦截器/过滤器);2不同人操作的板块不一样(权限)
shiro框架
1.shiro的登录验证(过滤器)
2.记住我汇关闭浏览器后再次打开系统不需要登录,就可以访问项目,本地cookie存储了用户信息;
shiro的rememberMe功能
Shiro 提供了记住我(RememerMe)的功能,比如访问一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问。
例如:当没有登录时,访问/main主页,会被拦截跳转到登录页面。当登录后是可以正常访问/man的主页的,如果使用了记住我功能,会在浏览器写入cookie,关掉浏览器不需要登录即可直接访问/main.
基本流程
1.首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe 的Cookie 写到客户端并保存下来;
2.关闭浏览器再重新打开,会发现浏览器还是记住你的;
3.访问一般的网页服务器端,仍然知道你是谁,且能正常访问;
4.但是,如果我们访问电商平台时,如果要查看我的订单或进行支付时,此时还 是需要再进行身份认证的,以确保当前用户还是你。
在springxml中配置cookie的设置和rememberMe管理器
1.配置记住我功能的cookie设置;
2.配置记住我功能的管理器;
3.在安全管理器中引用记住我功能的管理器;
<!--=================shiro相关配置====================-->
<!--配置shiro进行校验时的加密规则-->
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--加密规则-->
<property name="hashAlgorithmName" value="MD5" />
<!--是否加盐-->
<property name="hashSalted" value="true" />
<!--是否增加哈希算法进行散列-->
<!-- <property name="hashIterations" value="1024" />-->
</bean>
<!--配置自定义Realm-->
<bean id="myRealm" class="com.chen.shiro.MyRealm">
<property name="credentialsMatcher" ref="credentialsMatcher" />
</bean>
<!--=================记住我功能相关配置====================-->
<!--记住我功能的cookie设置-->
<bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!--设置cookie的属性名-->
<property name="name" value="rememberMe" />
<!--设置cookie存在根目录,可在同一应用服务器内共享-->
<property name="path" value="/" />
<!--通过JavaScript脚本将无法读取到Cookie信息,这样能有效的防止XSS攻击,让网站应用更
加安全-->
<property name="httpOnly" value="true" />
<!--设置cookie的失效时间为30天-->
<property name="maxAge" value="2592000" />
</bean>
<!--记住我功能的管理器配置-->
<bean id="rememberMeManager"
class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!--引用cookie设置-->
<property name="cookie" ref="simpleCookie" />
<!--默认AES算法,设置cookie的加密算法,采用的是base64的加密-->
<property name="cipherKey" value="#
{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}" />
</bean>
<!--配置安全管理器,使用自定义的Realm-->
<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--配置自定义的Realm-->
<property name="realm" ref="myRealm" />
<!--====引用rememberMe功能管理器====================-->
<property name="rememberMeManager" ref="rememberMeManager" />
</bean>
<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="shiroFilter"
class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 调用我们配置的权限管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 配置拦截后我们的登录请求地址 -->
<property name="loginUrl" value="/loginUI"/>
<!-- 如果您请求的资源不再您的权限范围,则跳转到错误页面 -->
<property name="unauthorizedUrl" value="/error"/>
<!-- 权限配置 -->
<!-- 权限配置
anon:任何人都可以访问; authc:必须是登录之后才能进行访问,不包括remember
me;
perms:指定过滤规则,可以自己拓展权限配置; roles:配置角色;
user:登录用户才可以访问,包含remember me; logout:退出
-->
<property name="filterChainDefinitions">
<value>
/=anon
/index = anon
/loginUI = anon
/login = anon
/WEB-INF/view/login.html = anon
/**/*.js=anon
/**/*.css=anon
/**=authc
/**=user
</value>
</property>
</bean>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input type="checkbox" name="rememberMe"> 记住我
</label>
</div>
</div>
</div>
1. 配置 boolean 类型的请求参数 rerememberMe ,并设置默认值是 false ;2.在封装 token 时,加入 rerememberMe 标记;
@RequestMapping("/login")
public String login(String uname, String pwd,@RequestParam(defaultValue = "false") boolean rememberMe, HttpSession session){
//1 获取 Subject 对象
Subject subject = SecurityUtils.getSubject();
//2 封装请求数据到 token 对象中
AuthenticationToken token = new UsernamePasswordToken(uname,pwd,rememberMe);
//3 调用 login 方法进行登录认证
try{
//验证主体是否能够登录
subject.login(token);
session.setAttribute("user",token.getPrincipal().toString());
return "main";
}catch(UnknownAccountException e){
System.out.println("用户名不存在!!");
return "login";
}catch(IncorrectCredentialsException e){
System.out.println("密码错误!");
return "login";
}catch(AuthenticationException e){
System.out.println("登录失败!");
return "login";
}
}
3、测试rerememberMe功能
1.不登录直接访问主页/main,shiro会拦截去到登录页;登录后可以正常访问主页/main;
2.登录时不勾选记住我,关闭浏览器访问主页/main还会拦截到登录页;
<a href="/logout">退出登录</a>
<property name="filterChainDefinitions">
<value>
/=anon
/index = anon
/loginUI = anon
/login = anon
/logout=logout
/WEB-INF/view/login.html = anon
/**/*.js=anon
/**/*.css=anon
/**=authc
/**=user
</value>
</property>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>主页</title>
<body>
<a href="/logout">退出登录</a>
<hr/>
<ul>
<li><a href="/user">用户管理</a></li>
<li><a href="/system">系统管理</a></li>
</ul>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
</head>
<body>
<ul>
<li><a href="/user/add">添加用户</a></li>
<li><a href="/user/delete">删除用户</a></li>
<li><a href="/user/find">查看用户</a></li>
<li><a href="/user/revise">修改用户</a></li>
</ul>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>系统管理界面</title>
</head>
<body>
<ul>
<li><a href="/system/set">系统设置</a></li>
<li><a href="/system/upd">系统修改</a></li>
</ul>
</body>
</html>
2、在控制器中使用@RequiresRoles("roles")注解来开启请求的角色验证
package com.zhan.controller;
import com.zhan.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
@Controller
public class UserController {
@RequestMapping("/login")
public String login(String uname, String pwd,@RequestParam(defaultValue = "false") boolean rememberMe, HttpSession session){
//1 获取 Subject 对象
Subject subject = SecurityUtils.getSubject();
//2 封装请求数据到 token 对象中
AuthenticationToken token = new UsernamePasswordToken(uname,pwd,rememberMe);
//3 调用 login 方法进行登录认证
try{
//验证主体是否能够登录
subject.login(token);
session.setAttribute("user",token.getPrincipal().toString());
return "main";
}catch(UnknownAccountException e){
System.out.println("用户名不存在!!");
return "login";
}catch(IncorrectCredentialsException e){
System.out.println("密码错误!");
return "login";
}catch(AuthenticationException e){
System.out.println("登录失败!");
return "login";
}
}
@RequiresRoles("user")
@RequestMapping("/user")
public String user(){
System.out.println("访问了用户管理请求");
return "userList";
}
@RequiresRoles("admin")
@RequestMapping("/system")
public String system(){
System.out.println("访问了系统管理请求");
return "systemManager";
}
@RequiresPermissions("user:add")
@RequestMapping("/user/add")
@ResponseBody
public String add(){
System.out.println("用户添加操作");
return "action user add...";
}
@RequiresPermissions("user:delete")
@RequestMapping("/user/delete")
@ResponseBody
public String delete(){
System.out.println("用户删除操作");
return "action user delete...";
}
@RequiresPermissions("user:find")
@RequestMapping("/user/find")
@ResponseBody
public String find(){
System.out.println("用户查询操作");
return "action user find...";
}
@RequiresPermissions("user:revise")
@RequestMapping("/user/revise")
@ResponseBody
public String revise(){
System.out.println("用户修改操作");
return "action user revise...";
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zhan.controller"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".html"/>
</bean>
<!--配置文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件的最大size-->
<property name="maxUploadSize" value="4194304"/>
</bean>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/fonts/**" location="/fonts/"/>
<mvc:resources mapping="/img/**" location="/img/"/>
<!-- <mvc:resources mapping="/layui/**" location="/layui/"/>-->
<!-- <mvc:resources mapping="/upload/**" location="/upload/"/>-->
<mvc:annotation-driven/>
<!--配置shiro bean生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!--配置自动创建代理-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<!-- 开启shiro注解的支持 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
MyRealm.java
package com.zhan.shiro;
import com.zhan.bean.User;
import com.zhan.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* token 里面是客户端发来的信息(包含输入的用户名和密码
* 自定义Realm,通过mybatis查询数据库的密码和盐值,让shiro进行身份验证
*/
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
UserService userService;
/**
* shiro 进行授权操作
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//打印
System.out.println("realm的授权认证方法被执行==============");
//1.创建角色和权限对象
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
//2.判断用户是否有对应的角色 或者 权限 有则放行 没有则抛出异常
//模拟操作---完成数据库操作
//2.1获取登录用户
String uname = principals.getPrimaryPrincipal().toString();
System.out.println("当前登录用户为:"+uname);
if(uname.equals("admin")){
//正常查询用户的角色(数据库使用set) 模拟赋予角色
String role = "admin";
authorizationInfo.addRole(role);
//授予权限正常查询该用户的权限,现在模拟赋予权限
authorizationInfo.addStringPermission("user:add");
authorizationInfo.addStringPermission("user:delete");
authorizationInfo.addStringPermission("user:find");
authorizationInfo.addStringPermission("user:revise");
}else{
//模拟添加角色
authorizationInfo.addRole("user");
authorizationInfo.addStringPermission("user:add");
authorizationInfo.addStringPermission("user:find");
}
//返回权限
return authorizationInfo;
}
/**
* shiro 进行认证操作
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//打印
System.out.println("realm的登录认证方法被执行==============");
//token 是主体传过来的身份令牌
//1.获取用户身份信息
String uname = token.getPrincipal().toString();
//2.调用业务层获取永固信息(数据库中)
User user = userService.selectByName(uname);
//判断并将数据完成封装
if(user!=null){
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
token.getPrincipal(),//令牌身份信息对象
user.getPwd(),//用户数据库的密码
ByteSource.Util.bytes(user.getSalt().getBytes()),//加密时的盐值
uname//用户名
);
return authenticationInfo;
}
return null;
}
}
配置权限,点击用户管理,用户为admin,可以点击四个权限;否则只有两个权限;
shiro授权异常处理
1.创建认证异常处理类,使用@ControllerAdvice和@ExceptionHandler实现特殊异常处理
package com.zhan.controller;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* shiro的异常处理
*/
@ControllerAdvice
public class ShiroExceptionHandler {
/**
* 处理没有权限的异常
* @param e
* @return
*/
@ExceptionHandler(UnauthorizedException.class)
public String handlerUnauthorizedException(UnauthorizedException e){
e.printStackTrace();
return "403";
}
/**
* 处理其他权限验证异常
* @param e
* @return
*/
@ExceptionHandler(AuthorizationException.class)
public String handlerAuthorizationException(AuthorizationException e){
e.printStackTrace();
return "403";
}
}
2.测试出现权限异常时是否会被异常处理执行
403.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>异常处理</title>
</head>
<body>
<h2>系统出现了异常,请联系管理员!!</h2>
</body>
</html>