1、加入需要用到的包
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2、编写application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.thymeleaf.cache=false
3、在页面表单中添加 “记住我” 功能的多选框
<input type="checkbox" name="rememberMe"/>记住我
4、编写MyShiroRealm配置类
package com.demo.rememberme.config;
import com.demo.rememberme.entity.User;
import com.demo.rememberme.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import javax.annotation.Resource;
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("1:身份认证-->MyShiroRealm.doGetAuthorizationInfo()");
//获取用户输入的信息
UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
String userName=token.getUsername();
String userPassword=new String(token.getPassword());
System.out.println("usrName:"+userName);
System.out.println("usrPassword:"+userPassword);
//通过用户名去数据库查询数据并判断输入是否正确
User user=userService.getUser(userName);
if(user==null){
throw new UnknownAccountException("账号不存在!");
}else if(!user.getUsrPassword().equals(userPassword)){
throw new IncorrectCredentialsException("密码不正确!");
}
//认证信息
SimpleAuthenticationInfo authorizationInfo=new SimpleAuthenticationInfo(user,user.getUsrPassword(),getName());
return authorizationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
5、编写AddPrincipalToSessionFilter配置类(用于解决session丢失)
package com.demo.rememberme.config;
import com.demo.rememberme.entity.User;
import com.demo.rememberme.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 解决session丢失
*/
public class AddPrincipalToSessionFilter extends OncePerRequestFilter {
@Autowired
UserService userService;
@Override
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
//查询当前用户的信息
Subject subject = SecurityUtils.getSubject();
//判断用户是不是通过自动登录进来的
if (subject.isRemembered()) {
User user2=(User)subject.getPrincipal();
String userName = user2.getUsrName();
System.out.println(userName+"..........");
if(userName==null){
return;
}
//根据用户名查询该用户的信息
User user=userService.getUser(userName);
if (user == null) return;
//由于是继承的OncePerRequestFilter,没办法设置session
//这里发现可以将servletReques强转为子类,所以使用request.getsiion())
HttpServletRequest request=(HttpServletRequest) servletRequest;
HttpSession session=request.getSession();
//当session为空的时候
if (session.getAttribute("user")==null){
//把查询到的用户信息设置为session,时效为3600秒
session.setAttribute("user",user);
session.setMaxInactiveInterval(3600);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
6、编写ShiroConfig配置类
package com.demo.rememberme.config;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/**
* cookie管理对象
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
@Bean
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
//setcookie()的第七个参数
//设为true后,只能通过http访问,javascript无法访问
//防止xss读取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//记住我cookie生效时间 ,单位秒
simpleCookie.setMaxAge(30);
return simpleCookie;
}
@Bean
public SecurityManager securityManager(){
System.out.println("1:securityManager..........");
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
@Bean
public MyShiroRealm myShiroRealm(){
System.out.println("2:myShiroRealm..........");
MyShiroRealm myShiroRealm=new MyShiroRealm();
return myShiroRealm;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
System.out.println("3:ShiroConfiguration.shiroFilter():配置权限控制规则");
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//登录提交地址
shiroFilterFactoryBean.setLoginUrl("/login");
//访问没有授权的资源
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
//拦截器
Map<String,String> filtrChainDefinitionMap=new LinkedHashMap<String,String>();
//匿名可以访问的地址
filtrChainDefinitionMap.put("/dologin","anon");
filtrChainDefinitionMap.put("/css/**","anon");
filtrChainDefinitionMap.put("/fonts/**","anon");
filtrChainDefinitionMap.put("/images/**","anon");
filtrChainDefinitionMap.put("/js/**","anon");
filtrChainDefinitionMap.put("/localcss/**","anon");
//配置退出(记住我状态下,可清除记住我的cookie)
filtrChainDefinitionMap.put("/logout","logout");
//所有路径必须授权访问(登录),且必须放在最后
filtrChainDefinitionMap.put("/**","user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filtrChainDefinitionMap);
//解决session丢失
Map<String, Filter> fmap = new HashMap<>();
fmap.put("addPrincipal", addPrincipalToSessionFilter());
shiroFilterFactoryBean.setFilters(fmap);
return shiroFilterFactoryBean;
}
/**
* Shiro自定义过滤器(解决session丢失)
* @return
*/
@Bean
public OncePerRequestFilter addPrincipalToSessionFilter() {
return new AddPrincipalToSessionFilter();
}
}
7、编写控制器登录方法
@RequestMapping(value = "/dologin")
public String login(boolean rememberMe,String usrName, String usrPassword, Map<String,Object> map, HttpSession session, HttpServletRequest request){
System.out.println("dologin..........");
try{
AuthenticationToken token=new UsernamePasswordToken(usrName,usrPassword);
//设置记住我
((UsernamePasswordToken) token).setRememberMe(rememberMe);
//调用Shiro进行认证
SecurityUtils.getSubject().login(token);
//从Shiro中拿出User对象
User user=(User)SecurityUtils.getSubject().getPrincipal();
System.out.println("----------"+((UsernamePasswordToken) token).isRememberMe());
session.setAttribute("user",user);
System.out.println("登录成功!");
}catch (Exception e){
//登录失败,返回登录页面
map.put("msg",e.getMessage());
return "login";
}
//重定向到另一个控制器请求返回主页面
return "redirect:/toMain";
}
8、测试
输入http://localhost:8080进入登录页面
勾选记住我后进行登录操作,进入浏览器的检查界面(F12)在Application中找到cookies,这时候就能看到有一条与页面中记住我多选框name相同的记录
登录成功后,关闭浏览器,重新打开,并输入需要登录后才能访问的地址,在未勾选记住我时会自动跳转到login页面,而勾选了记住我后则会找到cookie记录并进行自动登录操作