此案例是用IDEA创建的maven的web项目
使用shiro后为什么每次必须从登陆页面进,关键在于我们在spring-shiro.xml的匿名地址和公共地址的设置。具体看下面的spring-shiro.xml中的注释
4.0.0
com.qf
ssm0923
1.0-SNAPSHOT
war
ssm0923 Maven Webapp
http://www.example.com
UTF-8
1.7
1.7
5.0.8.RELEASE
org.apache.shiro
shiro-core
1.2.3
org.apache.shiro
shiro-web
1.2.3
org.apache.shiro
shiro-spring
1.2.3
org.apache.shiro
shiro-ehcache
1.2.3
org.apache.shiro
shiro-aspectj
1.2.3
org.apache.shiro
shiro-quartz
1.2.3
org.apache.shiro
shiro-ehcache
1.2.3
org.apache.shiro
shiro-quartz
1.2.3
javax.servlet
javax.servlet-api
3.1.0
provided
mysql
mysql-connector-java
5.1.38
org.mybatis
mybatis
3.2.8
org.springframework
spring-core
${springversion}
org.springframework
spring-web
${springversion}
org.springframework
spring-oxm
${springversion}
org.springframework
spring-tx
${springversion}
org.springframework
spring-jdbc
${springversion}
org.springframework
spring-webmvc
${springversion}
org.springframework
spring-aop
${springversion}
org.springframework
spring-context-support
${springversion}
org.springframework
spring-test
${springversion}
org.mybatis
mybatis-spring
1.2.2
jstl
jstl
1.2
com.alibaba
druid
1.0.9
com.github.pagehelper
pagehelper
4.1.6
com.fasterxml.jackson.core
jackson-databind
2.9.5
org.apache.poi
poi
3.6
javax
javaee-api
7.0
provided
commons-fileupload
commons-fileupload
1.3.1
commons-io
commons-io
2.4
joda-time
joda-time
2.9.9
org.mybatis.generator
mybatis-generator-core
1.3.5
org.slf4j
slf4j-api
1.7.5
org.slf4j
slf4j-log4j12
1.7.12
log4j
log4j
1.2.17
org.apache.poi
poi
3.6
ssm0923
maven-clean-plugin
3.1.0
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.5
src/main/resources/generatorConfig.xml
true
org.mybatis.generator
mybatis-generator-core
1.3.5
maven-resources-plugin
3.0.2
maven-compiler-plugin
3.8.0
maven-surefire-plugin
2.22.1
maven-war-plugin
3.2.2
maven-install-plugin
2.5.2
maven-deploy-plugin
2.8.2
-->
package com.qf.entity;
public class SysUser {
private String id;
private String userCode;
private String userName;
private String passWord;
private String salt;
private int locked;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public int getLocked() {
return locked;
}
public void setLocked(int locked) {
this.locked = locked;
}
}
package com.qf.util;
import com.qf.entity.SysUser;
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 java.util.ArrayList;
import java.util.List;
//自定义Realm一定要继承AuthorizingRealm。自定义MyRealm作用,验证前后台用户名和密码是否一致(即认证方法部分),给认证通过的用户赋予数据库中存储的对应的权限和角色信息(即授权方法部分)
public class MyRealm extends AuthorizingRealm {
@Override
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//在本案例中授权这个方法几乎没用到。主要用到的是下面的认证方法。写授权是作为示范
//创建集合添加用户对应的真实权限
List pers=new ArrayList();
pers.add("user:select");
pers.add("user:delete");
//创建向前台传递权限的SimpleAuthorizationInfo对象
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
//调用addStringPermissions方法将装有权限的集合作为参数赋给SimpleAuthorizationInfo对象传给前台
simpleAuthorizationInfo.addStringPermissions(pers);//有角色还可以在这加角色
return simpleAuthorizationInfo;
}
@Override
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String)authenticationToken.getPrincipal();//前台传过来的用户名,用于比对和数据库的用户名
if (username.equals("zhangsan")){//张三代表数据库中正确的用户名,这里为了例子简单,没有比对密码
SysUser sysUser=new SysUser();// SysUser使我们在entity中创建数据库中sys_user表对应的实体类。该对象用于存储登录用户信息,传给我们重写的过滤器MyFormFilter,用于将其存储到session中
sysUser.setUserName("张三");
return new SimpleAuthenticationInfo(sysUser,"123","myrealm");//123位数据库中的真实密码,直接通过SimpleAuthenticationInfo对象传给调用该方法的controller层
}
return null;
}
}
自定义Realm的类一定要继承AuthorizingRealm
package com.qf.util;
import com.qf.entity.SysUser;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
//写该类是为了在此处将登录成功的用户信息存储在session中。注意该类是继承FormAuthenticationFilter类重写,重写它的onLoginSuccess即shiro登录成功后的处理方法。
public class MyFormFilter extends FormAuthenticationFilter {
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
//得到认证通过的主体,获取自定义的Realm中SimpleAuthenticationInfo对象传的用户信息,通过getPrincipal获取
SysUser user=(SysUser)subject.getPrincipal();//获取自定义realm(即本案例的MyRealm)中SimpleAuthenticationInfo存储的用户名。获取密码是subject.getCredentials()
//然后将获取到的信息存储到Session中
HttpServletRequest httpServletRequest= WebUtils.toHttp(request);//将ServletRequest 转化成HttpServletRequest,ServletRequest似乎是HttpServletRequest的父类
HttpSession session = httpServletRequest.getSession();//获取session对象
session.setAttribute("user1",user);
return super.onLoginSuccess(token, subject, request, response);
}
}
文件的名字即MyFormFilter这个类名时随便取的但相应的位置一定要一致。
/favicon.ico=anon
/loginout=logout
/**=authc
package com.qf.controller;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
//登录
@Controller
public class UserController {
@RequestMapping("/tologin")
public String login(HttpServletRequest request) throws Exception {
//shiro+ssm框架中会处理成功的情况,我们在登录请求中之用处理用户名和密码比对失败的情况
//从FormAuthenticationFilter中获取认证失败的错误信息。因为shiro比对用户名和密码不一致,返回的不是false,而是异常,因此需要我们在处理失败的情况时捕获该异常,然后进行处理
String failure = (String) request.getAttribute("shiroLoginFailure");//shiroLoginFailure是shiro底层捕获比对错误的属性
if (failure!=null){//failure!=null意味着shiro底层比对failure产生了值,也就是发生了错误
if (UnknownAccountException.class.getName().equals(failure)){//shiro中前台和数据库用户名不一致,就是报UnknownAccountException异常
throw new Exception("用户名错误");
}else if (IncorrectCredentialsException.class.getName().equals(failure)){//shiro中前台和数据库密码不一致,就是报IncorrectCredentialsException异常
throw new Exception("密码错误");
}else {
throw new Exception("未知错误");
}
}
//没发生错,就跳转登录页面
return "login";//注意这里原本应该是return "/login.jsp",但因为我们在spring.xml文件中的第三步声明了前后缀,所以才写return "login"。
}
/* //退出。因为shiro自带的退出功能即 /loginout=logout。所以这几行代码就被注释了。不需要我们写处理退出请求的方法
@RequestMapping("/loginout")
public String out(HttpSession session){
System.out.println("----loginout");
session.invalidate();//使存储的session失效,清除session中的信息
return "redirect:/login.jsp";//注意这里跳转和上面return "login"跳转页面时的区别。return "login"是转发的跳转方式,转发是SSM默认的跳转方式。 return "redirect:/login.jsp"是重定向的跳转。即使在spring.xml文件中声明了前后缀,重定向依然要写全名。
}*/
}
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2019/9/23
Time: 14:07
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
index.jsp
当前用户:${sessionScope.user1.userName}
退出
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2019/9/25
Time: 10:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
权限不足