shiro简介

Apache Shiro™是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。

shiro四个核心模块

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

shrio三大核心组件

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:相 当 于 SpringMVC 中 的 DispatcherServlet

Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

shiro实现原理

应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager; 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

Shiro入门案例

1、使用骨架创建maven项目

2、在pom.xml中导入相关依赖

    <!--shiro依赖--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.1</version></dependency><!--log--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.25</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency>

3、编写shiro.ini配置文件

[users]
admin=123,admin,emp
zhangsan=123,seller
lisi=123,emp#三个用户admin,zhangsan,lisi
#三个角色admin(管理员),seller(经理),emp(普通员工)
#以用户管理来做对应的权限user:
[roles]
admin=*
seller=user:*
emp=user:query

4、测试类

public class ShiroTest {public static void main(String[] args) {//获取SecurityManager工厂,需要借助shiro.ini配置文件来初始化SecurityManagerFactory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");//通过SecurityManager工厂拿到SecurityManager实例SecurityManager securityManager = factory.getInstance();//把securityManager实例托管到SecurityUtiles工具类中SecurityUtils.setSecurityManager(securityManager);//通过SecurityUtils工具类获得SubjectSubject subject = SecurityUtils.getSubject();//判断用户是否已经登陆if(!subject.isAuthenticated()){//构建身份令牌UsernamePasswordToken token = new UsernamePasswordToken("lisi","123");//登陆,没有返回值,反正没报错就登陆成功,如果报错就登陆失败/*UnknownAccountException:用户名不存在IncorrectCredentialsException:密码错误*/try{subject.login(token);System.out.println("是否登陆:"+subject.isAuthenticated());System.out.println("欢迎:"+subject.getPrincipal());//角色校验System.out.println(subject.hasRole("seller"));//权限校验System.out.println(subject.isPermitted("user:query"));//退出登陆,用户登陆信息,会话信息,角色信息,权限信息等,全部清除subject.logout();}catch(UnknownAccountException e){System.out.println("用户名不存在,请检查用户名!");}catch(IncorrectCredentialsException e){System.out.println("密码错误,请检查密码!");}}}
}

Web

1、在pom.xml中导入相关依赖

  <dependencies><!--servlet--><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.1</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><!--springmvc--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.1.6.RELEASE</version></dependency><!--shiro--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.4.0</version></dependency><!--log--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.25</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency></dependencies><!--添加tomcat插件--><build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins></build>

2、编写springmvc-servlet.xml核心配置文件

    <!--开启注解驱动--><mvc:annotation-driven></mvc:annotation-driven><!--组件扫描--><context:component-scan base-package="cn.bdqn.controller"/><!--视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/"/><property name="suffix" value=".jsp"/></bean>

3、在web.xml中配置前端控制器

  <!--配置前端控制器--><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping>

4、在web.xml中配置ShiroFilter过滤器

  <!--ShiroFilter过滤器,在启动项目的时候,会去初始化Shiro环境--><filter><filter-name>shiroFilter</filter-name><filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class></filter><filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--在启动项目的时候,会去加载web-info或者classpath下的shiro.ini配置文件,然后构建WebSecurityManager--><listener><listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class></listener>

5、自定义异常解析器

因为登录失败没有返回值,直接抛出异常,对用户来说十分不友好

/*自定义异常解析器*/
public class MyExceptionResolver implements HandlerExceptionResolver {public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {e.printStackTrace();ModelAndView mv = new ModelAndView();if(e instanceof UnknownAccountException || e instanceof IncorrectCredentialsException){mv.setViewName("redirect:/user/login");}return mv;}
}

在springmvc-servlet.xml中注册该异常解析器

    <bean class="cn.bdqn.resolver.MyExceptionResolver"></bean>

6、控制器

@Controller
@RequestMapping("/user")
public class UserController {//去登录页面@RequestMapping(value = "/login",method = RequestMethod.GET)public String gotoLogin(){return "login";}@RequestMapping(value = "/login",method = RequestMethod.POST)public String login(User user){/*在web.xml中配置了shiroFilter之后,程序启动的时候就已经帮我们把shiro的环境构建好了,就是意味着SecurityManager实例已经构建,并且成功托管到了SecurityUtils工具类中*/Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());subject.login(token);return "main";}
}

7、编写shiro.ini配置文件

[users]
admin=123,admin,emp
zhangsan=123,seller
lisi=123,emp#三个用户admin,zhangsan,lisi
#三个角色admin(管理员),seller(经理),emp(普通员工)
#以用户管理来做对应的权限user:
[roles]
admin=*
seller=user:*
emp=user:query[main]
#如果没有登录,则跳转到这个路径
shiro.loginUrl = /user/login
#如果角色或者权限不足,则跳转到这个路径
shiro.unauthorizedUrl = /user/error
#退出之后跳转的路径
shiro.redirectUrl = /user/login[urls]
#路径 = 过滤器别名
#不登录也可以访问/user/login这个路径
/user/login = anon
#/user/main这个路径一定是要登录才能访问
/user/main = authc
#/user/delete这个路径,只有管理员或者经理才可以访问
/user/delete = authc,roles["admin"]
#/user/add这个路径,只有具备user:add权限才可以访问
/user/add = authc,perms["user:add"]
#执行/user/logout这个路径就执行了退出操作
/user/logout = logout

shiro标签

类似JSTL中的c:if,类型下面这种效果:

<c:if test="${user.role=='管理员'}">查看日志</c:if>

以前用JSTL之前需要先在页页面引入JSTL标准标签库。现在使用Shiro的标签也是一样需要引入shiro标签库

<%--引入shiro标签库--%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>

常用标签

1、<shiro:guest> 游客,没有记住我
2、<shiro:authenticated> 已经登陆
3、<shiro:notAuthenticated> 未登陆
4、<shiro:user> 已经登陆,或者记住我
5、<shiro:principal> 获取用户登陆名
6、<shiro:hasRole name="role_name"> 判断是指定角色
7、<shiro:lacksRole name="role_name"> 判断不是指定角色
8、<shiro:hasAnyRoles name="role_name1,role_name2"> 判断是否是其中一个角色
9、<shiro:hasPermission name="permission_name"> 判断是指定权限
10、<shiro:lacksPermisssion name="permission_name"> 判断不是指定角色

shiro加密

明文:用户输入的密码是啥,数据库存的就是啥,没有加密的就是明文

密文:用户输入的密码经过加密之后存储到数据中,加密了的数据就是密文

可逆加密:明文加密之后变成了密文,根据密文可以推算出明文,这就是可逆加密。不是很安全。

不可逆加密:明文加密之后变成了密文,无法从密文推算出明文。相对来说比较安全,建议使用不可逆加密。

shiro支持hash加密,md5和sha就是最常用的不可逆加密方式了

public class JmTest {public static void main(String[] args) {//明文密码String pwd = "123";/*.toString():十进制,相对base64来说密文长一些.toBase64():base64的编码格式,相对toString()来说密文短一些,建议使用* *///方式一,直接加密,手段太少,相对来说不是太安全String pwd1 = new Md5Hash(pwd).toString();System.out.println("明文是:"+pwd+",密文是:"+pwd1);String pwd2 = new Md5Hash(pwd).toBase64();System.out.println("明文是:"+pwd+",密文是:"+pwd2);//方式二,在明文密码的基础之上加点佐料(盐),来提高明文密码的复杂度String salt = UUID.randomUUID().toString();  //盐String pwd3 = new Md5Hash(pwd,salt).toBase64();System.out.println("明文是:"+pwd+",密文是:"+pwd3);/*类似下面的效果pwd += salt;String p1 = new Md5Hash(pwd).toBase64();*///方式三,在方式二的基础之上循环加密,从而提高加密的复杂度,建议使用int count = 1000; //循环加密次数String pwd4 = new Md5Hash(pwd,salt,count).toBase64();System.out.println("明文是:"+pwd+",密文是:"+pwd4);/*类型下面的效果pwd += salt;                              //第一次加盐String p1 = new Md5Hash(pwd).toBase64();  //第一次加密p1 += salt;                               //第二次加盐String p2 = new Md5Hash(p1).toBase64();   //第二次加密p2 += salt;                               //第三次加盐String p3 = new Md5Hash(p2).toBase64();   //第三次加密一直循环1000次*//*sha加密,跟MD5加密的写法是一样的,具体用哪个加密,自己决定*/String pwd5 = new Sha512Hash(pwd,salt,count).toBase64();System.out.println("明文是:"+pwd+",Sha密文是:"+pwd5);String pwd6 = new Sha256Hash(pwd,salt,count).toBase64();System.out.println("明文是:"+pwd+",Sha密文是:"+pwd6);}
}

SpringBoot+shiro整合

springboot+mybatisPlus+thymeleaf+shiro加密、认证、授权

数据库


drop table if exists t_user;
create table t_user(user_id int primary key auto_increment,username varchar(20) not null unique,password varchar(255) not null,salt varchar(255) not null
);
insert into t_user(username,password,salt) values("xc","123","1235");drop table if exists t_role;
create table t_role(role_id int primary key auto_increment,role_name varchar(20) not null
);
insert into t_role(role_name) values("seller");drop table if exists t_permission;
create table t_permission(permission_id int primary key auto_increment,permission_name varchar(50) not null
);
insert into t_permission(permission_name) values("user:query"),("user:add"),("user:delete");drop table if exists t_user_role;
create table t_user_role(user_id int references t_user(user_id),role_id int references t_role(role_id)
);
insert into t_user_role(user_id, role_id) VALUES (1,1);drop table if exists t_role_permission;
create table t_role_permission(role_id int references t_role(role_id),permission_id int references t_permission(permission_id)
);
insert into t_role_permission(role_id,permission_id) values(1,1),(1,2),(1,3);--认证,根据用户名查找对应的用户信息
select * from t_user where username="xc";--授权,根据用户名查找对应的角色名
select role_name from t_role r
inner join t_user_role ur on r.role_id=ur.role_id
inner join t_user u on u.user_id=ur.user_id
where u.username="xc";--授权,根据用户名查找对应的权限名
select permission_name from t_permission p
inner join t_role_permission rp on p.permission_id=rp.permission_id
inner join t_role r on r.role_id=rp.role_id
inner join t_user_role ur on r.role_id=ur.role_id
inner join t_user u on u.user_id=ur.user_id
where u.username="xc";

pom.xml

    <dependencies><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>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></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><!--mybatisPlus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.1.1</version></dependency><!--shiro依赖,也是需要shiro-web和shiro-coreshiro-spring需要依赖上面得两个jar包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency></dependencies>

application.properties

#数据库连接参数
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/yjhdb
spring.datasource.username=root
spring.datasource.password=1234spring.thymeleaf.cache=false

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {@TableId("user_id")private Integer userId;private String username;private String password;private String salt;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_role")
public class Role {@TableId("role_id")private Integer roleId;@TableField("role_name")private String roleName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_permission")
public class Permission {@TableId("permission_id")private Integer permissionId;@TableField("permission_name")private String permissionName;
}

数据访问层

@Mapper
public interface UserDao extends BaseMapper<User> {}
@Mapper
public interface RoleDao extends BaseMapper<Role> {@Select("select role_name from t_role r " +"inner join t_user_role ur on r.role_id=ur.role_id " +"inner join t_user u on u.user_id=ur.user_id " +"where u.username=#{username}")public Set<String> findByUsername(@Param("username") String username);
}
@Mapper
public interface PermissionDao {@Select("select permission_name from t_permission p " +"inner join t_role_permission rp on p.permission_id=rp.permission_id " +"inner join t_role r on r.role_id=rp.role_id " +"inner join t_user_role ur on r.role_id=ur.role_id " +"inner join t_user u on u.user_id=ur.user_id " +"where u.username=#{username}")public Set<String> findByUsername(@Param("username") String username);
}

业务逻辑层

public interface UserService {//根据用户名查询对应的用户信息public User findByUsername(String username);//注册public int add(User user);
}
public interface RoleService {//根据用户名查询对应的角色名public Set<String> findByUsername(String username);
}
public interface PermissionService {//根据用户名查询对应的权限名public Set<String> findByUsername(String username);
}

实现类

@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserDao userDao;@Overridepublic User findByUsername(String username) {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("username",username);return userDao.selectOne(queryWrapper);}@Overridepublic int add(User user) {//盐String salt = UUID.randomUUID().toString();user.setSalt(salt);//加密String password = new Md5Hash(user.getPassword(),salt, SpringTools.SHIRO_JM_COUNT).toBase64();user.setPassword(password);return userDao.insert(user);}
}
@Service
public class RoleServiceImpl implements RoleService {@Resourceprivate RoleDao roleDao;@Overridepublic Set<String> findByUsername(String username) {return roleDao.findByUsername(username);}
}
@Service
public class PermissionServiceImpl implements PermissionService {@Resourceprivate PermissionDao permissionDao;@Overridepublic Set<String> findByUsername(String username) {return permissionDao.findByUsername(username);}
}

自定义Realm

SecurityManager默认的Realm是IniRealm,这个Realm只能去加载shiro.ini文件中的数据,但是这个项目中并没有这个文件。而是把这个文件的用户,角色,权限信息都存入到了数据库中,所以IniRealm就已经没有意义了。需要自己定义一个去查询数据库的Realm,然后注入给SecurityManager使用。

/*** 自定义Realm*/
public class MyRealm extends AuthorizingRealm {@Resourceprivate UserService userService;@Resourceprivate RoleService roleService;@Resourceprivate PermissionService permissionService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//做授权,肯定需要先知道是谁登录了String username = (String)principals.getPrimaryPrincipal();//调用service层,根据用户名查询对应的角色和权限的方法Set<String> roles = roleService.findByUsername(username);Set<String> permissions = permissionService.findByUsername(username);SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);simpleAuthorizationInfo.setStringPermissions(permissions);return simpleAuthorizationInfo;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//获取用户登录输入的用户名String username = (String)token.getPrincipal();//调用service层根据用户名查询对应的用户的方法User user = userService.findByUsername(username);if(user == null){return null;}return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());;}
}

Shiro配置类

/*** Shiro配置类*/
@Configuration
public class ShiroConfig {/*注册密码比对器(什么方式加密,迭代多次,是否采用十进制)*/@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//设置加密方式hashedCredentialsMatcher.setHashAlgorithmName("MD5");//设置加密迭代次数hashedCredentialsMatcher.setHashIterations(SpringTools.SHIRO_JM_COUNT);//是否采用十进制hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);return hashedCredentialsMatcher;}/*注册自定义Realm,加入到bean容器*/@Beanpublic MyRealm myRealm(){MyRealm myRealm = new MyRealm();//把自定义的密码比对器注册给RealmmyRealm.setCredentialsMatcher(hashedCredentialsMatcher());return myRealm;}/*配置SecurityManager*/@Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//把自定义Realm注入给SecurityManagersecurityManager.setRealm(myRealm());return securityManager;}/*Filter工厂,设置对应的过滤条件和跳转条件*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);Map<String,String> map = new HashMap<String,String>();//执行/user/logout这个路径就执行了退出操作map.put("/user/logout","logout");//不登录也可以访问/user/login这个路径map.put("/user/login","anon");//不登录也可以访问/user/add这个路径map.put("/user/add","anon");///user/main这个路径一定是要登录才能访问map.put("/user/main","authc");///user/delete这个路径,只有经理才可以访问map.put("/user/delete","roles[seller]");//...//配置未登录跳转的路径shiroFilterFactoryBean.setLoginUrl("/user/login");//角色和权限不足时跳转的路径shiroFilterFactoryBean.setUnauthorizedUrl("/user/error");//退出跳转的路径shiroFilterFactoryBean.setSuccessUrl("/user/login");shiroFilterFactoryBean.setFilterChainDefinitionMap(map);return shiroFilterFactoryBean;}/*注册自定义异常解析器*/@Beanpublic MyExceptionResolver myExceptionResolver(){return new MyExceptionResolver();}}

控制器

@Controller
@RequestMapping("/user")
public class UserController {@Resourceprivate UserService userService;/*跳转去注册页面*/@GetMapping("add")public String add(){return "add";}/*注册功能*/@PostMapping("add")public String add(User user){userService.add(user);return "redirect:/user/login";}/*跳转去登录页面*/@GetMapping("/login")public String login(){return "login";}/*登录功能*/@PostMapping("/login")public String login(User user){Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());subject.login(token);return "redirect:/user/main";}/*跳转去main页面*/@GetMapping("/main")public String main(){return "main";}/*删除操作*/@GetMapping("/delete")@ResponseBodypublic String delete(){return "删除成功";}}

页面模板

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body><h1>欢迎登录</h1><form action="login" method="post"><p>用户名:<input name="username"></p><p>密  码:<input type="password" name="password"></p><p><input type="submit" value="登录"></p></form>
</body>
</html>

记住我

在登录之后,把用户的信息记录在Cookie中。以后访问,可以不登录,但是服务器也能识别出来。如果需要执行一些敏感的操作再要求用户登录即可,用户体验好。

如果需要使用记住我,只需要在登录之前开启记住我的功能即可

    /*登录功能*/@PostMapping("/login")public String login(User user){Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());//记住我token.setRememberMe(true);subject.login(token);return "redirect:/user/main";}

默认Cookie的最大过期时间是365天。而且里面有两个同名的Cookie(RememberMe),其中value为deleteMe的cookie是为了登录的时候先把原来存在的Cookie删除,在写入最新的Cookie,保存每次登录之后Cookie都是最新的

如果你的项目中就是使用默认的Cookie那就已经完成了。但是如果你想对默认的Cookie做修改,就要自己配置了,比如最大过期时间不想那么长

    /*自定义Cookie配置*/@Beanpublic SimpleCookie simpleCookie(){//创建名为rememberMe的CookieSimpleCookie cookie = new SimpleCookie("rememberMe");//设置cookie的最大过期时间,单位是秒,10天=864000秒cookie.setMaxAge(864000);//设置只能http请求访问,默认就是truecookie.setHttpOnly(true);return cookie;}/*cookie管理对象,rememberMe管理器*/@Beanpublic CookieRememberMeManager rememberMeManager(){CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();//给CookieRememberMeManager注入自定义的CookiecookieRememberMeManager.setCookie(simpleCookie());return cookieRememberMeManager;}/*配置SecurityManager*/@Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//把自定义Realm注入给SecurityManagersecurityManager.setRealm(myRealm());//把自定义rememberMe管理器注入给SecurityManagersecurityManager.setRememberMeManager(rememberMeManager());return securityManager;}

记住我前端标签

thymeleaf模板默认不支持shiro标签的,如果需要在thymeleaf中使用shiro标签,需要额外引入对应的依赖

1、pom.xml

        <dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>1.2.1</version></dependency>

2、在shiro配置文件中配置方言

    /*配置shiro方言,让thymeleaf支持shiro标签*/@Beanpublic ShiroDialect shiroDialect(){return new ShiroDialect();}

3、页面使用

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><meta charset="UTF-8"><title>个人中心</title>
</head>
<body><!--游客模块--><p shiro:guest="">你好,游客!</p><!--已登录模块--><p shiro:authenticated="">欢迎:<span shiro:principal=""></span></p><!--已经登录或者记住我--><p shiro:user="">我记住你了:<span shiro:principal=""></span></p></body>
</html>

Session会话管理

shiro是一个安全框架,所以对状态的保持是必须的。shiro提供了一整套session管理方案

核心对象

1、SimpleSession:负责完成session基本功能

2、SimpleSessionFactory:负责生产SimpleSession的工厂

3、SessionDAO:类似UserDAO,负责session的增删改查

4、DefaultSessionManager:session管理者,管理SimpleSessionFactory和SessionDAO

Session参数修改

    /*自定义session-Cookie配置*/@Beanpublic SimpleCookie simpleCookie1(){//创建名为rememberMe的CookieSimpleCookie cookie = new SimpleCookie("JSESSIONID");//设置cookie的最大过期时间,单位是秒,默认-1,表示当初会话有效。0表示删除当前cookiecookie.setMaxAge(-1);//设置只能http请求访问,默认就是truecookie.setHttpOnly(true);return cookie;}/*session管理器*/@Beanpublic DefaultWebSessionManager defaultWebSessionManager(){DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();//给DefaultWebSessionManager注入自定义的CookiedefaultWebSessionManager.setSessionIdCookie(simpleCookie1());//session全局超时时间,默认30分钟,单位毫秒。10000L=10秒defaultWebSessionManager.setGlobalSessionTimeout(10000L);return defaultWebSessionManager;}/*配置SecurityManager*/@Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//把自定义Realm注入给SecurityManagersecurityManager.setRealm(myRealm());//把自定义rememberMe管理器注入给SecurityManagersecurityManager.setRememberMeManager(rememberMeManager());//把自定义session管理器注入给SecurityManagersecurityManager.setSessionManager(defaultWebSessionManager());return securityManager;}

Session监听

session监听就是监控session的3个状态,创建,过期,停止

过期:判断过期的方式是在session被创建的时候记录创建时间,然后跟现在的时间对比,判断这个session是否已经过期。session不会自动报告过期,需要再次访问或者检测器检测时才会识别这个session是否已经过期

停止:只有在logout或session.stop()时才会触发

/*** 自定义session监听器*/
public class MySessionListener extends SessionListenerAdapter {//session创建时触发@Overridepublic void onStart(Session session) {System.out.println("--session创建--");}//session停止时触发@Overridepublic void onStop(Session session) {System.out.println("--session停止--");}//session过期时触发@Overridepublic void onExpiration(Session session) {System.out.println("--session过期--");}
}

在shiro配置类中添加相关配置

    /*注册自定义sessino监听器*/@Beanpublic List<SessionListener> sessionListener(){List<SessionListener> listeners = new ArrayList<>();listeners.add(new MySessionListener());//...如果有多个监听器,直接往listeners集合中添加就可以了return listeners;}/*session管理器*/@Beanpublic DefaultWebSessionManager defaultWebSessionManager(){DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();//给DefaultWebSessionManager注入自定义的CookiedefaultWebSessionManager.setSessionIdCookie(simpleCookie1());//session全局超时时间,默认30分钟,单位毫秒。10000L=10秒defaultWebSessionManager.setGlobalSessionTimeout(10000L);//给session管理者注入自定义session监听器defaultWebSessionManager.setSessionListeners(sessionListener());return defaultWebSessionManager;}

Session检测

用户直接关闭浏览器,session是否过期无法得知,意味着session就不能停止。所以shiro提供了session的检测机制,可以定时发起检测,识别session是否过期。session检测是默认开启的,默认时间是1小时,所以只需要修改检测的时间间隔就可以了。

    /*session管理器*/@Beanpublic DefaultWebSessionManager defaultWebSessionManager(){DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();//给DefaultWebSessionManager注入自定义的CookiedefaultWebSessionManager.setSessionIdCookie(simpleCookie1());//session全局超时时间,默认30分钟,单位毫秒。10000L=10秒defaultWebSessionManager.setGlobalSessionTimeout(10000L);//给session管理者注入自定义session监听器defaultWebSessionManager.setSessionListeners(sessionListener());//开启session检测,默认开启,可以不写defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);//设置检测的时间间隔,单位毫秒,默认1小时,15000L=15秒defaultWebSessionManager.setSessionValidationInterval(15000L);return defaultWebSessionManager;}

Shiro注解开发

1、@RequiresAuthenthentication:表示当前Subject已经通过login进行身份验证;即 Subject.isAuthenticated()返回 true2、@RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的3、@RequiresGuest:表示当前Subject没有身份验证或者通过记住我登录过,即是游客身份4、@RequiresRoles(value = {"admin","user"},logical = Logical.AND):表示当前Subject需要角色admin和user5、@RequiresPermissions(value = {"user:delete","user:b"},logical = Logical.OR):表示当前Subject需要权限user:delete或者user:b

注解就是就是一个标识,不会自动生效,所以要使用注解,需要做一些配置

    /*开启shiro注解,借助aop来实现*/@Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();//开启shiro注解advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;}/*开启aop注解支持*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();//给AuthorizationAttributeSourceAdvisor注入SecurityManagerauthorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}

shiro+redis

pom.xml

        <!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--shiro+redis缓存插件--><dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>2.4.2.1-RELEASE</version></dependency><!--工具包--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!--jedis--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency>

application.properties

#redis相关配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8
spring.redis.timeout=10000

ShiroConfig.java

    /*注入redis参数*/@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;/*配置shiro的redis管理器*/@Beanpublic RedisManager redisManager(){RedisManager redisManager = new RedisManager();redisManager.setHost(host);redisManager.setPort(port);return redisManager;}/*配置shiro的redis缓存管理器*/@Beanpublic RedisCacheManager cacheManager(){RedisCacheManager cacheManager = new RedisCacheManager();//给RedisCacheManager注入自定义的redis管理器cacheManager.setRedisManager(redisManager());return cacheManager;}/*配置shiro的redisSessionDAO*/@Beanpublic RedisSessionDAO redisSessionDAO(){RedisSessionDAO redisSessionDAO = new RedisSessionDAO();//给RedisSessionDAO注入自定义的redis管理器redisSessionDAO.setRedisManager(redisManager());return redisSessionDAO;}/*session管理器*/@Beanpublic DefaultWebSessionManager defaultWebSessionManager(){DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();//给DefaultWebSessionManager注入自定义的CookiedefaultWebSessionManager.setSessionIdCookie(simpleCookie1());//session全局超时时间,默认30分钟,单位毫秒。10000L=10秒defaultWebSessionManager.setGlobalSessionTimeout(10000L);//给session管理者注入自定义session监听器defaultWebSessionManager.setSessionListeners(sessionListener());//开启session检测,默认开启,可以不写defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);//设置检测的时间间隔,单位毫秒,默认1小时,15000L=15秒defaultWebSessionManager.setSessionValidationInterval(15000L);//给DefaultWebSessionManager注入自定义的redisSessionDAOdefaultWebSessionManager.setSessionDAO(redisSessionDAO());return defaultWebSessionManager;}

SpringBoot整合Shiro详细用法相关推荐

  1. springboot整合shiro和session的详细过程和自定义登录拦截器

    文章目录 1.shiro依赖 2.shiro配置 shiro过滤器配置: 关联自定义的其他管理器 自定义会话工厂: 3.登陆时记录用户信息 4.shiro一些工具类的学习 5.自定义登录拦截器 shi ...

  2. springboot整合shiro(超详细,你想要的都在这了)

    Springboot整合Shiro 文章目录 pom依赖 前端页面(thymeleaf整合shiro) thymeleaf中shiro标签解释 数据库(整合mybatis) 理解shiro的几个组成部 ...

  3. 补习系列(6)- springboot 整合 shiro 一指禅

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

  4. SpringBoot 整合Shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  5. SpringBoot整合Shiro搭建登录注册认证授权权限项目模板

    主要内容: 1 SpringBoot整合Shiro安全框架; 2 Shiro主要学习内容总结;(执行流程.主要对象接口.注意事项等) 3 Redis实现对权限信息缓存; ! 温馨提示: 想要快速搭Sh ...

  6. SpringBoot整合Shiro实现权限管理与登陆注册

    前言 Shiro解决了什么问题? 互联网无非就是一些用户C想要使用一些服务S的资源去完成某件事,S的资源不能说给谁用就给谁用,因此产生了权限的概念,即C必须有权限才能操作S的资源.S如何确定C就是C呢 ...

  7. 降龙十八掌之 springboot整合shiro(含MD5加密)

    java学习爱好者 2019-05-27 16:21:00 开发环境: 1.mysql - 5.7.21 2.navicat(mysql客户端管理工具) 3.idea 2017 4.jdk9 5.to ...

  8. 补习系列- springboot 整合 shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  9. 补习系列(6)-SpringBoot 整合Shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

最新文章

  1. 计算机专业python教材_计算机专业几本必看的书!
  2. StemBlock pytorch实现
  3. 爬虫进阶教程:极验(GEETEST)验证码破解教程
  4. windows下钩子的使用
  5. np.random.seed的有效期及固定的种子会有固定的顺序
  6. TACACS +和RADIUS比较
  7. 导入jar包到Maven本地仓库(maven install jar)
  8. Java if语句深度解析
  9. linux的grub损坏,如何利用Grub命令启动损坏的Linux系统?
  10. 常见30种数学建模模型_11.12|认识数学模型与数学建模许可
  11. CDN---共享单车算啥,阿里云发布共享网络黑科技PCDN,降低视频行业75%的成本
  12. spring学习笔记一(基于xml)
  13. 腾讯QQ浏览器 10.0.932.400 正式版
  14. Akash,全球首个去中心化云计算
  15. 95后软件测试工程师成长指南
  16. RXTX for java在arm上的使用
  17. 【sketchup 2021】草图大师的辅助建模工具1【量角器与文字、尺寸标注与三维字、实体工具】
  18. 直方图python_Python数据可视化的例子——直方图和核密度曲线
  19. stm32f103c8t6 FLASH模拟EEPROM
  20. mobaxterm配置Tunneling隧道连接服务器

热门文章

  1. [Wi-Fi抓包篇]3. WireShark ——抓wlan口包的方法
  2. BUUCTF WEB [BJDCTF2020]ZJCTF,不过如此
  3. LTE学习:PHICH(一)
  4. C/C++---字符分布分割得到数字,适用于STM32/ESP32等等
  5. java.lang.IllegalArgumentException: Invalid character found in method name [0x160x030x0
  6. 《今日美国》Twitter账号遭遇黑客攻击
  7. C语言中的字符串与字符串函数
  8. Python+PyCharm+PyTorch+Cuda/GPU 安装步骤
  9. 【RBF预测】基于时空 RBF-NN 实现混沌时间序列预测附matlab代码
  10. 细说;(function ($, undefined){ })(jQuery); 的使用