【shiro】认证鉴权

shiro框架

shiro:强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理

在这里插入图片描述

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心 脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会 话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实 现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的 哪些功能;

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

SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个 组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境; 所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;

SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可 以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的 Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到 缓存中后可以提高访问的性能 Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

简单使用

认证:
1.根据配置文件创建SecurityManagerFactory
2.通过工厂获取SecurityManager
3.将SecurityManager绑定到当前运行环境
4.从当前运行环境中构造subject
5.构造shiro登录的数据
6.主体登陆
  • 依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.3</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.2</version>
</dependency>
  • 配置文件
[users]
#用户名=密码,角色名
zhangsan=123456,role1,role2
lisi=123456,role2
[roles]
#角色
#角色名=权限列表
role1=user:save,user:update
role2=user:find
  • 使用
package cn.itcast.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Before;
import org.junit.Test;

public class ShiroTest02 {
    private SecurityManager securityManager;
    @Before
    public void init() {
        //1.根据配置文件创建SecurityManagerFactory
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test-2.ini");
        //2.通过工厂获取SecurityManager
        SecurityManager securityManager = factory.getInstance();
        //3.将SecurityManager绑定到当前运行环境
        SecurityUtils.setSecurityManager(securityManager);
    }
    @Test
    public void testLogin() {
        Subject subject = SecurityUtils.getSubject();
        String username = "lisi";
        String password = "123456";
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        subject.login(token);
        //登录成功之后,完成授权
        //授权:检验当前登录用户是否具有操作权限,是否具有某个角色
        System.out.println(subject.hasRole("role1"));   // false
        System.out.println(subject.isPermitted("user:save"));  // false
    }
}

realm实现自定义认证授权

  • 配置文件
[main]
#声明realm
permReam=cn.itcast.shiro.PermissionRealm
#注册realm到securityManager中
securityManager.realms=$permReam
  • 自定义realm
package cn.itcast.shiro;

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 java.util.ArrayList;
import java.util.List;

/**
 * 自定义realms对象
 *      继承AuthorizingRealm
 *          重写方法
 *              doGetAuthorizationInfo:授权
 *                  获取到用户的授权数据(用户的权限数据)
 *              doGetAuthenticationInfo:认证
 *                  根据用户名密码登录,将用户数据保存(安全数据)
 *
 */
public class PermissionRealm extends AuthorizingRealm {

    /**
     * 自定义realm名称
     */
    public void setName(String name) {
        super.setName("permissionRealm");
    }

    //授权:授权的主要目的就是根据认证数据获取到用户的权限信息

    /**
     * principalCollection:包含了所有已认证的安全数据
     * AuthorizationInfoInfo:授权数据
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权方法");
        //1.获取安全数据  username,用户id
        String username = (String)principalCollection.getPrimaryPrincipal();
        //2.根据id或者名称查询用户

        //3.实际应从数据库查询用户的角色和权限信息,没有数据库,以下为构造数据
        List<String> perms = new ArrayList<>();
        perms.add("user:save");
        perms.add("user:update");
        List<String> roles = new ArrayList<>();
        roles.add("role1");
        roles.add("role2");

        //4.构造返回
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //设置权限集合
        info.addStringPermissions(perms);
        //设置角色集合
        info.addRoles(roles);
        return info;
    }

    //认证:认证的主要目的,比较用户名和密码是否与数据库中的一致
    //将安全数据存入到shiro进行保管
    //参数:authenticationToken登录构造的usernamepasswordtoken
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证方法");
        //1.构造uptoken
        UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
        //2.获取输入的用户名密码
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());
        
        //3.根据用户名查询数据库,正式系统查询
        
        //4.比较密码和数据库中的密码是否一致(密码可能需要加密)
        if("123456".equals(password)) {
            //5.如果成功,向shiro存入安全数据
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,getName());//1.安全数据,2.密码。3。当前realm域名称
            return info;
        }else{
            //6.失败,抛出异常或返回null
            throw new RuntimeException("用户名或密码错误");
        }
    }
}
  • 测试
package cn.itcast.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Before;
import org.junit.Test;

public class ShiroTest03 {
    private SecurityManager securityManager;
    @Before
    public void init() {
        //1.根据配置文件创建SecurityManagerFactory
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-test-3.ini");
        //2.通过工厂获取SecurityManager
        SecurityManager securityManager = factory.getInstance();
        //3.将SecurityManager绑定到当前运行环境
        SecurityUtils.setSecurityManager(securityManager);
    }
    @Test
    public void testLogin() {
        Subject subject = SecurityUtils.getSubject();
        String username = "zhangsan";
        String password = "123456";
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        //执行login-->realm域中的认证方法
        subject.login(token);
        //授权:-->realm域中的授权方法
        System.out.println(subject.hasRole("role1"));
        System.out.println(subject.isPermitted("user:save"));
    }
}

springboot使用

  • 依赖
<!--shiro与redis整合实现sessionDao -->
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.0.0</version>
</dependency>
<!--shiro和spring整合-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!--shiro核心包-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
</dependency>
<!--shiro与redis整合-->
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.0.0</version>
</dependency>
  • 配置
server:
  port: 8081
spring:
  application:
    name: ihrm-company #指定服务名
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shiro_db?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true
  redis:
    host: 127.0.0.1
    port: 6379
  • 自定义reaml实现认证鉴权
package cn.itcast.shiro.realm;

import cn.itcast.shiro.domain.Permission;
import cn.itcast.shiro.domain.Role;
import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
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.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    @Override
    public void setName(String name) {
        super.setName("customRealm");
    }

    /**
     * 授权方法
     *  操作的时候,判断用户是否具有相应的权限
     *      先认证 -- 安全数据
     *      再授权 -- 根据安全数据获取用户具有的所有操作权限
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 1、获取已认证的用户数据
        User user = (User) principalCollection.getPrimaryPrincipal();
        // 2、根据用户数据获取用户的权限信息(所有角色,所有权限)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> roles = new HashSet<>(); // 所有角色
        Set<String> perms = new HashSet<>(); // 所有权限
        for (Role role : user.getRoles()) {
            roles.add(role.getName());
            for (Permission perm : role.getPermissions()) {
                perms.add(perm.getName());
            }
        }
        info.setStringPermissions(perms);
        info.setRoles(roles);
        return info;
    }

    /**
     * 认证方法
     *  参数:传递的用户名和密码
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取登录的用户名密码(token)
        UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());
        //2.根据用户名查询数据库
        User user = userService.findByName(username);
        //3.判断用户是否存在或者密码是否一致
        if (null != user && user.getPassword().equals(password)){
            // 4.如果一致返回安全数据
            // 构造方法:安全数据,密码,realm域名
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
            return info;
        }
        //5.不一致,返回null(抛出异常)
        return null;
    }
}
  • 添加配置类
package cn.itcast.shiro;

import cn.itcast.shiro.realm.CustomRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfiguration {

    // 1-创建realm
    @Bean
    public CustomRealm getReaml(){
        return new CustomRealm();
    }
    // 2-创建安全管理器
    @Bean
    public SecurityManager getSecurityManager(CustomRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }
    /**
     * 3-配置shiro的过滤器工厂
     * 在web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制.
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirofilter(SecurityManager securityManager){
        // 1-创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        // 2-设置安全管理器
        filterFactory.setSecurityManager(securityManager);
        // 3-通用配置(登录页面,未授权跳转的页面)
        filterFactory.setLoginUrl("/autherror?code=1"); // 跳转url
        filterFactory.setUnauthorizedUrl("/autherror?code=2");
        // 4-设置过滤器集合
        /**
         * 设置所有过滤器:有顺序的map
         *  key = 拦截的url地址
         *  value = 过滤器类型
         */
        Map<String,String> filterMap = new LinkedHashMap<>();
        filterMap.put("/user/home","anon");//当前请求地址可以匿名访问
        //具有某中权限才能访问
        //使用过滤器的形式配置请求地址的依赖权限,不具备指定的权限,跳转到setUnauthorizedUrl地址
        //filterMap.put("/user/home","perms[user-home]"); 
        //使用过滤器的形式配置请求地址的依赖角色
        //filterMap.put("/user/home","roles[系统管理员]");

        filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
        filterFactory.setFilterChainDefinitionMap(filterMap);
        return filterFactory;
    }

    // 开启对shior注解的支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}
  • 测试controller
package cn.itcast.shiro.controller;

import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;
import java.util.Enumeration;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    //个人主页
    @RequestMapping(value = "/user/home")
    public String home() {
        return "访问个人主页成功!";
    }

    //添加
    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String add() {
        return "添加用户成功";
    }
	
    //查询
    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String find() {
        return "查询用户成功";
    }
	
    //更新
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String update(String id) {
        return "更新用户成功";
    }
	
    //删除
    @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
    public String delete() {
        return "删除用户成功";
    }
	
	//用户登录
	@RequestMapping(value="/login")
    public String login(String username,String password) {
		System.out.println("用户登录");
		try {
            /**
             * 密码加密:
             *  shiro提供的md加密
             *      参数1:加密内容
             *          111111 --- abcd
             *      参数2:盐(加密的混淆字符串) 使用登录用户名
             *          111111+混淆字符串
             *      参数3:加密次数 3
             */
            password = new Md5Hash(password, username, 3).toString();
            // 1.构造登录令牌
            UsernamePasswordToken upToken = new UsernamePasswordToken(username, password);
            // 2、获取subject
            Subject subject = SecurityUtils.getSubject();
            // 3、调用subject进行登录
            subject.login(upToken);
            return "登录成功";
		}catch (Exception e){
		    e.printStackTrace();
		    return "用户名或密码错误!";
		}
    }
    @RequestMapping(value = "/autherror",method = RequestMethod.GET)
    public String autherror(int code) {
        return code==1?"未登录":"未授权";
    }
}
shiro注解使用

配置类中需要添加开启注解的配置。

  • 开启
// 开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(securityManager);
    return advisor;
}
  • controller测试方法
/**
 * 说明:
 *  @RequiresPermissions() --访问此方法需要的权限
 *  @RequiresRoles() --访问此方法需要的角色
 *
 *  过滤器鉴权:权限不匹配跳转到通过setUnauthorizedUrl()设置的地址
 *  注解:权限不匹配则抛出异常【AuthorizationExceptior】
 */
//个人主页
@RequiresPermissions("user-home")
@RequiresRoles("system-admin")
@RequestMapping(value = "/user/home")
public String home() {
    return "访问个人主页成功!";
}

shiro会话管理

在这里插入图片描述

在shiro里所有的用户的会话信息都会由Shiro来进行控制,shiro提供的会话可以用于JavaSE/JavaEE环境,不依赖于任何底层容器,可以独立使用,是完整的会话模块。通过Shiro的会话管理器(SessionManager )进行统一的会话管理。

SessionManager(会话管理器)∶管理所有Subject的session包括创建、维护、删除、失效、验证等工作。SessionManager是顶层组件,由SecurityManager管理。shiro提供了三个默认实现:

  • DefaultSessionManager:用于JavaSE环境
  • ServletContainerSessionManager:用于Web环境,直接使用servlet容器的会话。【默认使用】
  • DefaultWebSessionManager:用于web环境,自己维护会话(自己维护着会话,直接废弃了Servlet容器的会话管理,即session可根据自己需要指定存储位置)。

测试默认session存储:

//登录成功后,打印所有session内容
@RequestMapping(value="/show")
public String show(HttpSession session) {
    // 获取session中所有的键值
    Enumeration<?> enumeration = session.getAttributeNames();
    // 遍历enumeration中的
    while (enumeration.hasMoreElements()) {
        // 获取session键值
        String name = enumeration.nextElement().toString();
        // 根据键值取session中的值
        Object value = session.getAttribute(name);
        // 打印结果
        System.out.println("<B>" + name + "</B>=" + value + "<br>/n");
    }
    return "查看session成功";
}
应用场景

在这里插入图片描述

  • 通过认证中心把信息存入redis并返回sessionid
  • 访问其余服务带上sessionid(放到请求头或coockie中),服务通过会话管理区redis中获取信息,完成认证。
使用readis存储会话

依据上面springboot使用例子。

  • 依赖
<!--shiro与redis整合-->
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.0.0</version>
</dependency>
  • 配置文件指定redis地址
spring:
  redis:
    host: 127.0.0.1
    port: 6379
  • 定义会话管理器
package cn.itcast.shiro.session;

import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

/**
 * @Author
 * @Date 2021-08-10 00:00
 */
public class CustomSessionManager extends DefaultWebSessionManager {
    /**
     * sessionid通过头信息携带:
     *  通过请求头获取:Authorization:sessionid
     * 指定sessionId的获取方式
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        // 获取请求头Authorization数据
        String id = WebUtils.toHttp(request).getHeader("Authorization");
        if (StringUtils.isEmpty(id)){
            // 没有携带,生成新sessionId
            return super.getSessionId(request,response);
        }else {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        }
    }
}
  • shiro配置类中
package cn.itcast.shiro;

import cn.itcast.shiro.realm.CustomRealm;
import cn.itcast.shiro.session.CustomSessionManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author
 * @Date 2021-08-09 00:24
 */
@Configuration
public class ShiroConfiguration {

    // 1-创建realm
    @Bean
    public CustomRealm getReaml(){
        return new CustomRealm();
    }
    // 2-创建安全管理器
    @Bean
    public SecurityManager getSecurityManager(CustomRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);

        // 将自定义会话管理器告知安全管理器
        securityManager.setSessionManager(sessionManager());
        // 将自定义redis缓存管理器注册到安全管理器
        securityManager.setCacheManager(cacheManager());

        return securityManager;
    }

    // 3-配置shiro的过滤器工厂

    /**
     * 在web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制.
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirofilter(SecurityManager securityManager){
        // 1-创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        // 2-设置安全管理器
        filterFactory.setSecurityManager(securityManager);
        // 3-通用配置(登录页面,未授权跳转的页面)
        filterFactory.setLoginUrl("/autherror?code=1"); // 跳转url
        filterFactory.setUnauthorizedUrl("/autherror?code=2");
        // 4-设置过滤器集合
        /**
         * 设置所有过滤器:有顺序的map
         *  key = 拦截的url地址
         *  value = 过滤器类型
         */
        Map<String,String> filterMap = new LinkedHashMap<>();
        filterMap.put("/user/home","anon");//当前请求地址可以匿名访问
        //具有某中权限才能访问
        //使用过滤器的形式配置请求地址的依赖权限
        //filterMap.put("/user/home","perms[user-home]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址
        //使用过滤器的形式配置请求地址的依赖角色
        //filterMap.put("/user/home","roles[系统管理员]");

        filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问
        filterFactory.setFilterChainDefinitionMap(filterMap);
        return filterFactory;
    }

    // 开启对shior注解的支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    /**
     * 1-redis控制器,操作redis
     */
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        return redisManager;
    }
    /**
     * 2-sessionDao
     */
    public RedisSessionDAO redisSessionDAO(){
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }
    /**
     * 3-会话管理器
     */
    public DefaultWebSessionManager sessionManager(){
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }
    /**
     * 4-缓存管理器
     */
    public RedisCacheManager cacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }
    /**
     * 5-将自定义会话管理器告知安全管理器(查看上面安全管理器设置)
     */
}
  • 存储的安全数据需要实现接口:AuthCachePrincipal (redis和shiro插件包提供的接口,使安全数据可以存到redis中)
package cn.itcast.shiro.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.crazycake.shiro.AuthCachePrincipal;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * 用户实体类
 */
@Entity
@Table(name = "pe_user")
@Getter
@Setter
public class User implements Serializable, AuthCachePrincipal {
    private static final long serialVersionUID = 4297464181093070302L;
    /**
     * ID
     */
    @Id
    private String id;
    private String username;
    private String password;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name="pe_user_role",joinColumns={@JoinColumn(name="user_id",referencedColumnName="id")},
            inverseJoinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}
    )
    private Set<Role> roles = new HashSet<Role>();//用户与角色   多对多
    
    @Override
    public String getAuthCacheKey() {
        return null;
    }
}
  • controller测试
//用户登录
@RequestMapping(value="/login",method = RequestMethod.GET)
public String login(String username,String password) {
    System.out.println("用户登录");
    try {
        	/**
             * 密码加密:
             *  shiro提供的md加密
             *      参数1:加密内容
             *          111111 --- abcd
             *      参数2:盐(加密的混淆字符串) 使用登录用户名
             *          111111+混淆字符串
             *      参数3:加密次数 3
             */
        password = new Md5Hash(password, username, 3).toString();

        // 1.构造登录令牌
        UsernamePasswordToken upToken = new UsernamePasswordToken(username, password);
        // 2、获取subject
        Subject subject = SecurityUtils.getSubject();
        // 获取session
        String sid = (String) subject.getSession().getId();
        // 3、调用subject进行登录
        subject.login(upToken);
        return "登录成功:"+sid;
    }catch (Exception e){
        e.printStackTrace();
        return "用户名或密码错误!";
    }
}

浏览器和postman属于两个不同的会话。如何实现登录认证?

浏览器访问【/login】接口返回登录的sessionid,在postman中直接添加请求头:【Authorization:sessionid】 访问相关接口如:【/user】即可正常访问,实现了登录认证。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值