Shiro

目录

原理/流程

thymeleaf使用shiro标签

 jdbcrealm

自定义realm

对密码进行加密

加盐

用户授权

方式一

方式二

方式三

缓存

session管理(15毫秒没有操作就退出)

Remember me  记住我

多realm


原理/流程

1.核心组件

  1》subject 封装了用户名密码,用户权限,用户角色等信息

  2》securitymanager负责进行校验用户名密码,权限等操作

  3》realm,相当于securitymanager的数据源,去数据库查询,将权限,是否登录成功等信息提供给secutirymanager,

thymeleaf使用shiro标签

1 必须导入依赖

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

2  在shiroconfig.java文件中配置方言支持(就是将shrio的语法转为tymeleaf的语法)

 //配置方言
    @Bean
    public ShiroDialect getshirodialect() {
        return new ShiroDialect();
    }

3 在html中添加依赖

<html lang="en" xmlns:th="http://www.thymeleaf.org"

      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

1 设置不同的登录状态显示不同的标签

需要shiro的拦截器将该页面设置为未登录也可以访问,或者默认就是未登录可访问

<!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>
<h1>登录成功</h1>
<shiro:guest>
    <div>欢迎游客访问,</div><a href="/">登录</a>
</shiro:guest>

<shiro:user>
    已经登录的用户!!
</shiro:user>
</body>
</html>

2.获取当前登录的用户名

<shiro:principal/>

3/如果用户有某个角色就显示对应的字

<shiro:hasRole name="admin">超级管理员</shiro:hasRole>

<shiro:hasRole name="cmanager">仓库管理</shiro:hasRole>

<shiro:hasRole name="xmanager">销售人员</shiro:hasRole>

<shiro:hasRole name="kmanager">客服人员</shiro:hasRole>

<shiro:hasRole name="zmanager">行政人员</shiro:hasRole>

4/用户的角色有某个权限就显示对应的字

<ul>      

<shiro:hasPermission name="sys:x:find"><li><a href="#">查找销售记录</a> </li></shiro:hasPermission>

</ul>

总结:

<!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>
<h1>登录成功</h1>
<shiro:guest>
    <div>欢迎游客访问,</div><a href="/">登录</a>
</shiro:guest>

<shiro:user>
    已经登录的用户!!
   用户名是 <shiro:principal/>
    <div>职位是:
    <shiro:hasRole name="admin">超级管理员</shiro:hasRole>
    <shiro:hasRole name="cmanager">仓库管理</shiro:hasRole>
    <shiro:hasRole name="seller">销售人员</shiro:hasRole>
    <shiro:hasRole name="kmanager">客服人员</shiro:hasRole>
    <shiro:hasRole name="zmanager">行政人员</shiro:hasRole>
    </div>
    <div>
    具有的权限如下:
    <shiro:hasPermission name="order-add"><li><a href="#">添加订单</a> </li></shiro:hasPermission>
    <shiro:hasPermission name="order-del"><li><a href="#">删除订单</a> </li></shiro:hasPermission>
    <shiro:hasPermission name="order-list"><li><a href="#">查询订单</a> </li></shiro:hasPermission>
  <shiro:hasPermission name="user_query"><li><a href="#">张三不具有这个权限,所以应该不显示</a> </li></shiro:hasPermission>
    </div>
</shiro:user>
</body>
</html>
 

 jdbcrealm

 

1.在数据库中创建表,表名必须是users   user_roles  roles_permissions 

字段名分别是

DROP TABLE
IF EXISTS `users`;

CREATE TABLE users (
	id INT PRIMARY KEY auto_increment,
	username VARCHAR (60) NOT NULL,
	PASSWORD VARCHAR (20) NOT NULL,
	password_salt VARCHAR (20)
);

INSERT INTO users (username, PASSWORD)
VALUES
	('zhangsan', '123');

INSERT INTO users (username, PASSWORD)
VALUES
	('lisi', '456');

INSERT INTO users (username, PASSWORD)
VALUES
	('wangwu', '789');

DROP TABLE
IF EXISTS `user_roles`;

CREATE TABLE user_roles (
	id INT PRIMARY KEY auto_increment,
	username VARCHAR (60) NOT NULL,
	role_name VARCHAR (100) NOT NULL
);

INSERT INTO user_roles (username, role_name)
VALUES
	('zhangsan', 'admin');

INSERT INTO user_roles (username, role_name)
VALUES
	('lisi', 'manager');

INSERT INTO user_roles (username, role_name)
VALUES
	('wangwu', 'saler');

DROP TABLE
IF EXISTS `roles_permissions`;

CREATE TABLE roles_permissions (
	id INT PRIMARY KEY auto_increment,
	rolename VARCHAR (60) NOT NULL,
	permission VARCHAR (100) NOT NULL
);

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('admin', '*');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('manager', 'sys:m:update');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('manager', 'sys:m:select');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('manager', 'sys:m:delete');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('manager', 'sys:m:add');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('saler', 'sys:s:update');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('saler', 'sys:s:delete');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('saler', 'sys:s:add');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('saler', 'sys:s:select');

INSERT INTO roles_permissions (rolename, permission)
VALUES
	('saler', 'sys:k:manager');

1.在application.yaml中配置数据库的数据源信息

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/my_db_01
    username: root
    password: mysql

2.在shiroconfig中配置 jdbcrealm,并将其作为realm绑定到SecurityManager,在查询时,shiro会自己链接数据源,查询登录和角色信息(前提是数据库的字段 表名要和规定的一致,看上面)

    @Autowired
    DataSource dataSource;   
 @Bean
    public JdbcRealm getjdbcRealm(){
        JdbcRealm jdbcRealm=new JdbcRealm();
//        只需要给数据源,回自动查找数据库
        jdbcRealm.setDataSource(dataSource);
//        默认只开启认证功能,手动开启授权功能
        jdbcRealm.setPermissionsLookupEnabled(true);
       return jdbcRealm;
    }
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager();
        SecurityManager.setRealm(getjdbcRealm());
        return SecurityManager;
    }

调用不变,更换realm只是后面数据的来源变了,前面验证该怎么用还是怎么用

同样的,登录成功不报错,不成功报错

 try {
            subject.login(usernamePasswordToken);
            return "success";
        }
        catch (Exception e)
        {
            System.out.println(e);
            System.out.println("拦截到了错误的账号或者密码");
            return "error1";
        }

 

自定义realm

自定义的realm可以读取数据库中的加密,加盐了的数据。

realm就是数据源,负责从数据库中查询数据,不应该由我们调用,我们写好之后,配置给securitymanager,由它自动调用

  1. 自定义realm类要继承AuthorizingRealm
  2. 重写doGetAuthorizationInfo查询用户的角色和权限     doGetAuthenticationInfo获取用户信息
  3. 重写getname()方法(因为可能自定义多个realm,要区分) 

 doGetAuthenticationInfo方法要:获取数据源,链接数据库,从用户表中查询用户名对应的信息,能查到就返回用户信息,查不到就返回null(并不进行登录验证,只是将该用户名对应的用户信息查询出来)

 //获取认证信息
    @Override
   public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String uname = usernamePasswordToken.getUsername();
        User user = userMapper.checklogin(uname);
        System.out.println(user);
        if (user == null) {
            return null;
        } else {
            AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getname());
            return info;
        }
    }

doGetAuthorizationInfo查询用户具有的权限 和角色信息

   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//        获取当前的用户名
        String username = (String) principalCollection.iterator().next();
//        查询权限信息
        Set<String> permissions =powerMapper.selectpermissions(username);
        System.out.println("permissions"+permissions);
//        查询角色信息
        Set<String> roles = powerMapper.selectrole(username);
        System.out.println("roles如下:"+roles);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        设置角色信息
        info.setRoles(roles);
//        设置权限信息
        info.setStringPermissions(permissions);
        return info;
    }

配置好后的myrealm

package com.example.demo79ini.Config;


import com.example.demo79ini.mapper.PowerMapper;
import com.example.demo79ini.mapper.UserMapper;
import com.example.demo79ini.pojo.User;
import com.sun.org.apache.bcel.internal.generic.RETURN;
import org.apache.shiro.authc.*;
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.apache.tomcat.util.net.openssl.ciphers.Authentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.security.sasl.AuthorizeCallback;
import javax.sound.sampled.Line;
import java.util.Set;

@Configuration
public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserMapper userMapper;
    @Autowired
    PowerMapper powerMapper;
    public String getname() {
        return "MyRealm";
    }

//    //    获取授权信息 将当前用户的权限和角色信息查询出来,这里的已经登录了用户,所以直接获取权限,角色即可
//    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//        获取当前的用户名
        String username = (String) principalCollection.iterator().next();
//        查询权限信息
        Set<String> permissions =powerMapper.selectpermissions(username);
        System.out.println("permissions"+permissions);
//        查询角色信息
        Set<String> roles = powerMapper.selectrole(username);
        System.out.println("roles如下:"+roles);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        设置角色信息
        info.setRoles(roles);
//        设置权限信息
        info.setStringPermissions(permissions);
        return info;
    }
    //获取认证信息
    @Override
   public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String uname = usernamePasswordToken.getUsername();
        User user = userMapper.checklogin(uname);
        System.out.println(user+"从数据库中查出来的user");
        if (user == null) {
            return null;
        } else {
            AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getPassword_salt()), getname());
            return info;
        }
    }
}

可以查询出用户对应的角色信息

 查询出对应的权限信息

 数据库的查询语句如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo79ini.mapper.PowerMapper">
    <select id="selectrole" resultType="java.lang.String" parameterType="string">
        select role_name
        from tb_users
                 INNER JOIN tb_roles_users on tb_users.id = tb_roles_users.uid
                 INNER JOIN tb_roles on tb_roles_users.rid = tb_roles.role_id
        where tb_users.username = #{username}
    </select>
    <select id="selectpermissions" resultType="java.lang.String">
        select permission_code
        from tb_users
                 INNER JOIN tb_roles_users on tb_users.id = tb_roles_users.uid
                 INNER JOIN tb_roles on tb_roles_users.rid = tb_roles.role_id
                 INNER JOIN tb_permissions_roles on tb_permissions_roles.role_id = tb_roles.role_id
                 INNER JOIN tb_permissions on tb_permissions.permission_id = tb_permissions_roles.permission_id
        where tb_users.username = #{username}
    </select>
</mapper>

设置好之后需要在securitymanager中进行绑定

    @Autowired
    MyRealm myRealm;

    @Bean
    public DefaultWebSecurityManager getdefaultWebSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(myRealm);
        return defaultWebSecurityManager;
    }

对密码进行加密

MD5算法,是不能解密的,但是如果明文一样,加密次数一样,则生成的md5密文也是一样的 所以要在检查用户密码前,将用户的密码进行相同次数的MD5加密,如果和数据库中的一致,则是对的用户名和密码。

查询加密后的密码

1.设置  HashedCredentialsMatcher类   在类中设置要使用的加密算法和加密算法要执行的次数 

    @Bean
    public HashedCredentialsMatcher gethHashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//        指定加密的算法
        matcher.setHashAlgorithmName("md5");
//        设置加密算法的执行次数
        matcher.setHashIterations(1);
        return matcher;
    }

2.在myrealm中绑定 HashedCredentialsMatcher类

 @Bean
    public MyRealm MyRealm1() {
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(gethHashedCredentialsMatcher());
        return myRealm;
    }

3.注意securitymanager类中,一定要使用刚才设置了setCredentialsMatcher的 realm

4.配置好加密类之后,在用户登录时,会自动给密码加密指定的次数,后再匹配数据库中的数据

    @RequestMapping("/check_login")
    public String check_loging(String uname,String upwd)
    {
        UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(uname,upwd);
        Subject subject= SecurityUtils.getSubject();
//        System.out.println(subject+"contoller调用时的subject");
        try {
            subject.login(usernamePasswordToken);
            return "success";
        }
        catch (Exception e)
        {
//            System.out.println(e);
            System.out.println("拦截到了错误的账号或者密码");
            return "error1";
        }
    }

 向数据库中添加加密后的数据

 @RequestMapping("/register")
    public String register(String uname,String upwd){
        System.out.println(uname+upwd+"到达注册页面的用户名和密码");
        Md5Hash md5Hash=new Md5Hash(upwd);
        System.out.println("一次加密"+md5Hash+"");
        userMapper.adduser(new User( uname,md5Hash+"",null));
        return "login";
    }

加盐

在加密的基础上,可以让密码更安全。

就是在加密前,给密码添加一个随机的前缀或者是后缀,之后再进行加密

一般盐随机生成,一个密码对应一个盐,所以盐和密码要一同存储在数据库中

注意一定要在加密的基础上,即该realm在shiroconfig中配置了加密算法,

   @Bean
    public HashedCredentialsMatcher gethHashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//        指定加密的算法
        matcher.setHashAlgorithmName("md5");
//        设置加密算法的执行次数
        matcher.setHashIterations(1);
        return matcher;
    }
 @Bean
    public ManagerRealm managerRealm()
    {
        ManagerRealm managerRealm=new ManagerRealm();
        managerRealm.setCredentialsMatcher(gethHashedCredentialsMatcher());
        return  managerRealm;
    }

查询加盐后的数据

在myrealm中,查询数据库,会把密码和对应的盐查出来,在 AuthenticationInfo 中,需要将密码和对应的 盐作为参数,返回给sercurityManger。

@Override
   public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String uname = usernamePasswordToken.getUsername();
        User user = userMapper.checklogin(uname);
        System.out.println(user+"从数据库中查出来的user");
        if (user == null) {
            return null;
        } else {
//只有这一句
            AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getPassword_salt()), getname());
            return info;
        }
    }

给密码加盐加密后填入数据库

    @RequestMapping("/register")
    public String register(String uname,String upwd){
        System.out.println(uname+upwd+"到达注册页面的用户名和密码");
        int num=new Random().nextInt(90000)+10000;//随机生成字符串
        Md5Hash md5Hash=new Md5Hash(upwd,num+"");
        String pwd_md5=md5Hash+"";
        System.out.println("一次加盐加密"+pwd_md5+"盐是"+num);
        userMapper.adduser(new User( uname,pwd_md5,num+""));
        return "login";
    }

用户授权

设置用户具有某些权限才可以访问某些页面

方式一

ShiroConfig类中设置

 @Bean
    public ShiroFilterFactoryBean shiroFilter() {
//        anon匿名用户可以访问
//        authc认证用户可以访问
//        user 使用了rememerme的可以访问
//        perms对应的权限可以访问
//        对应的角色可以访问
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        filter.setSecurityManager(getdefaultWebSecurityManager());
        Map<String, String> filterMap = new HashMap<>();
//       不设置请求的权限,就是anon,不登录也可以访问
        filterMap.put("/login", "anon");
        filterMap.put("/", "anon");
//        filterMap.put("/success", "authc");
        filter.setFilterChainDefinitionMap(filterMap);
//       下面的路径是需要springmvc进行解析的 不能直接跳转到该页面
//        @RequestMapping("/noquanxian")
        //   设置默认的登录页面,这个好像不起作用,springmvc拦截/请求即可
        filter.setLoginUrl("/noquanxian");
        //    设置未授权,但是访问了该页面就跳转到
        filter.setUnauthorizedUrl("/noquanxian");
        return filter;
    }

方式二

步骤:

1...配置springshiro注解的支持,在shiroconfig中 添加两个类,没必要理解,记住就好

 //配置注解管理权限 ,记住就好
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(getdefaultWebSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator autoproxycreator = new DefaultAdvisorAutoProxyCreator();
        autoproxycreator.setProxyTargetClass(true);
        return autoproxycreator;
    }

2...在controller中添加@RequiresPermissions注解

@RequestMapping("c_delete")
@RequiresPermissions("sys:c:delete")
public String c_delete(){
    System.out.println("删除客户信息");
    return "c_delete";
}

用户访问要访问的页面,如果具有sys:c:delete权限,则可以正常访问,如果不具有,会报500错误。

使用此种方式拦截,如果访问了未经授权的页面,会报如下错误

方式三

手动验证  不同于前两种,必须要拦截一个路径,这种方法不需要拦截路径,所以可以用在service层,用户必须具有某个权限才可以进行操作 

@Service
public class UserService {

    public String c_update(Subject subject){
        if(subject.isPermitted("sys:c:update"))
        {
            System.out.println("更新客户信息");
            return "c_update";
        }
        else
        {
            System.out.println("权限不足,不能更新");
            return "error1";
        }
    }
}

缓存

发现登录查询只执行一次,但是权限查询,有多少个查询标签,就执行多少次该方法

 

为了解决这种情况,需要使用缓存

1添加依赖

     <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2配置缓存策略

在resouce目录下,创建一个xml文件,名字随便起,一般叫ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         dynamicConfig="false"
         updateCheck="false">
    <!--如果内存满了,会存储在这个内容-->
    <diskStore path="c:\Temp"/>
    <!--    name随便起,timetoliveseconds存活时间    maxentrites最大的条数-->
    <cache
            name="users"
            maxEntritesLocalHeap="1000"
            timeToLiveSeconds="300"/>
    <!--    eternal是否永久存储   timeToIdleSeconds允许空闲多久的数据被删除-->
    <!--    eternal="true" 表示常驻缓存-->
    <!--    overflowToDisk="false" 缓存数据太多了,不向磁盘存储,而是清除老的数据-->
    <defaultCache
            name="defaultCache"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            maxElementsOnDisk="100000"
            diskPersistent="false"
            diskExpriyThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>

    <!--memoryStoreEvictionPolicy 缓存的淘汰策略,最近最少使用  fifo最老数据-->
</ehcache>

3加入缓存管理

    // 配置缓存管理
    @Bean
    public EhCacheManager getehacche() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml ");
        return ehCacheManager;
    }

在myrealm中设置这个缓存

    @Bean
    public DefaultWebSecurityManager getdefaultWebSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(GetMyRealm1());
        defaultWebSecurityManager.setCacheManager(getehacche());
        return defaultWebSecurityManager;
    }

效果:

只执行一次权限查询了

session管理(15毫秒没有操作就退出)

可以粗略的理解为认证信息,是放在session中的,可以手动新建session,设置session的过期时间,这样就实现了用户登录后,多长没有进行操作,就自动退出登录。

1.需要自定义session管理器(15毫秒没有操作就退出)

    @Bean
    public DefaultWebSessionManager getdefaultWebSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(15 * 1000);//单位是毫秒
        return sessionManager;
    }

2.在securitymanger中绑定这个session

@Bean
    public DefaultWebSecurityManager getdefaultWebSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(GetMyRealm1());
        defaultWebSecurityManager.setCacheManager(getehacche());
        defaultWebSecurityManager.setSessionManager(getdefaultWebSessionManager());
        return defaultWebSecurityManager;
    }

Remember me  记住我

在关闭浏览器/关闭页面后,重新打开该页面,只要是登录过,不需要输入账号密码,直接登录。

shiro将用户对页面访问的权限分为三个级别:

未认证,可以访问的界面  login.html  register.html

曾经认证过,可以访问的界面  个人基本信息

必须经过认证,才可以访问的界面  转账,必须登录才可以访问

1.设置自己的session

//    设置自己定义的session
    @Bean
    public DefaultWebSessionManager getdefaultWebSessionManager(){
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(15*1000);
        System.out.println("执行了自己的session");
        return  sessionManager;
    }
  1. 将自己定义的session管理器赋值给securitymanager
//    sercuritymanager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    defaultWebSecurityManager.setCacheManager(getehacche());
    defaultWebSecurityManager.setRealm(MyRealm1());
    defaultWebSecurityManager.setSessionManager(getdefaultWebSessionManager());
    return defaultWebSecurityManager;
}

2.在shiroconfig中设置  曾经登录过可以访问的页面(user)

 public ShiroFilterFactoryBean shiroFilter() {
//        anon匿名用户可以访问
//        authc认证用户可以访问
//        user 使用了rememerme的可以访问
//        perms对应的权限可以访问
//        对应的角色可以访问
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        filter.setSecurityManager(defaultWebSecurityManager());
        Map<String, String> filterMap = new HashMap<>();
        filterMap.put("/index", "user");//记住密码或者登录后才可访问
        filterMap.put("/login.html", "anon");
        filterMap.put("/register.html", "anon");
        filterMap.put("/register", "anon");
        filterMap.put("/static/**", "anon");
        filterMap.put("/logintest", "anon");
        filterMap.put("/**", "authc");
        filterMap.put("/exit", "logout");
        filterMap.put("/c_update", "perms[sys:c:update]");
        filter.setFilterChainDefinitionMap(filterMap);
        //   设置默认的登录页面
        filter.setLoginUrl("/login.html");
        //    设置未授权,但是访问了该页面就跳转到
        filter.setUnauthorizedUrl("/lesspermission.html");
        return filter;
    }

3在shiroconfig中配置remembermemanager

    @Bean
    public CookieRememberMeManager getcookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(30 * 24 * 60 * 90);
        cookieRememberMeManager.setCookie(cookie);
        return cookieRememberMeManager;
    }

Securitymanager中添加这个类

    @Bean
    public DefaultWebSecurityManager getdefaultWebSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(GetMyRealm1());
        defaultWebSecurityManager.setCacheManager(getehacche());
        defaultWebSecurityManager.setSessionManager(getdefaultWebSessionManager());
        defaultWebSecurityManager.setRememberMeManager(getcookieRememberMeManager());
        return defaultWebSecurityManager;
    }
  1. 前端需要添加一个单选框,是否记住密码  在验证用户登录的时候添加一句   usernamePasswordToken.setRememberMe(true 或者false);  即可
@Service
public class UserService  {
    public void checklogin(String username,String password,boolean rememberme) throws Exception{
        System.out.println(rememberme+"rememberme");
        Subject subject= SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);
        usernamePasswordToken.setRememberMe(rememberme);
        subject.login(usernamePasswordToken);
    }
}

多realm

1多个不同的数据源 oracle  mysql  多个数据源分别进行处理  存放的都是用户表,一个存了一部分,就需要每个数据源都要访问

1链式处理 就是配置两个realm

在配置security的过程中,添加这两个realm即可

@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    Collection<Realm> realms=new ArrayList<>();
    realms.add(getmyrealm2());
    realms.add(getmyRealm());
    defaultWebSecurityManager.setRealms(realms);
    return defaultWebSecurityManager;
}

2多个数据源,选择一个进行验证  普通用户和管理员 一张用户表,一张管理员表,就是有两个realm,需要根据logintype判断到哪个realm进行认证。

分支处理 根据不同的条件,执行不同的realm

1。。因为官方的usernamepasswordtoken只有两个属性,用户名和密码,所以需要自己定义一个token,继承自usernamepasswordtoken

package com.example.shiro78.config;

import org.apache.shiro.authc.UsernamePasswordToken;

public class Mytoken extends UsernamePasswordToken {
    private String loginType;

    public Mytoken(String username, String password, String loginType) {
        super(username, password);
        this.loginType = loginType;
    }

    @Override
    public String toString() {
        return "Mytoken{" +
                "loginType=" + loginType + getUsername()+"mytoken的值";
    }
}
2。。因为官方的 将realm绑定到security的方法,是设置多少个realm,就绑定多少个,而我们要根据输入的logintype方法,确定要绑定哪个realm,所以要重写这个方法,即 MyModulaRealmAutherticator类中的doAuthenticate方法,就是写一个类,继承自MyModulaRealmAutherticator,然后重写doAuthenticate,这样该类其他的方法的代码都不会变,然后在sercuritymanager中将这个类设置为绑定的方法即可
package com.example.shiro78.config;



import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authc.pam.ModularRealmAuthenticator;

import org.apache.shiro.realm.Realm;



import java.util.Collection;



public class MyModulaRealmAutherticator extends ModularRealmAuthenticator {

System.out.println("执行了自己定义的doauthenticte方法");

Mytoken mytoken= (Mytoken) authenticationToken;

String logintype=mytoken.getLoginType();

Collection<Realm> realms = this.getRealms();

Collection<Realm> typerealms=new ArrayList<>();

for (Realm realm:realms)

{

    if(realm.getName().startsWith(logintype))

    {

        System.out.println(realm.getName());

        typerealms.add(realm);

    }

}

if(typerealms.size() == 1)

{

    return this.doSingleRealmAuthentication((Realm) typerealms.iterator().next(),authenticationToken);

}

else{

    return this.doMultiRealmAuthentication(typerealms, authenticationToken);

}

将新的类注入ioc容器

@Bean
public MyModulaRealmAutherticator getMyModulaRealmAutherticator() {
    MyModulaRealmAutherticator myModulaRealmAutherticator = new MyModulaRealmAutherticator();
    return myModulaRealmAutherticator;
}

sercuritymanager中将这个类设置为绑定的方法即可

@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    defaultWebSecurityManager.setAuthenticator(getMyModulaRealmAutherticator());
    Collection<Realm> realms = new ArrayList<>();
    realms.add(getmyrealm2());
    realms.add(getmyRealm());
    defaultWebSecurityManager.setRealms(realms);
    return defaultWebSecurityManager;
}

                
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值