美文网首页
9.shiro整合springboot

9.shiro整合springboot

作者: __元昊__ | 来源:发表于2019-07-10 16:25 被阅读0次

1.先把springboot和mybatis整合好,然后整合jsp或者thymeleaf。这里用jsp

2.pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lyh</groupId>
    <artifactId>shiro_springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shiro_springboot</name>
    <description>Demo project for Spring Boot</description>
    <packaging>war</packaging>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--最新版本,匹配spring Boot1.5 or higher-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.21</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>

        <!--JavaServer Pages Standard Tag Library,JSP标准标签库-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <!--内置tocat对Jsp支持的依赖,用于编译Jsp-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <!--<scope>provided</scope>-->
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.springboot的application.properties配置文件

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url = jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name = com.mysql.jdbc.Driver

#指定bean所在包
mybatis.type-aliases-package=com.lyh.shiro_springboot.domain
#指定映射文件
mybatis.mapper-locations=classpath:mapper/*.xml

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

4.自定义realm

import com.lyh.shiro_springboot.dao.UserMapper;
import com.lyh.shiro_springboot.domain.User;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserMapper userMapper;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user=(User)principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if(user.getRoles()!=null && !user.getRoles().equals("")){
            info.addRole(user.getRoles());
        }
        if(user.getPermission()!=null && !user.getPermission().equals("")){
            info.addStringPermission(user.getPermission());
        }
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = authenticationToken.getPrincipal().toString();
        User userByName = userMapper.getUserByName(username);
        ByteSource salt = ByteSource.Util.bytes(userByName.getPassword_salt());
        return new SimpleAuthenticationInfo(userByName,userByName.getPassword(),salt,getName());
    }
}

5.shiro的配置类编写代码

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class shiroConfig {

    //配置shiro的具体拦截器
    @Bean(name="shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String, String> fMap = new HashMap<>();
        //拦截页面
        fMap.put("/gologin", "anon");
        fMap.put("/tologin", "anon");
        fMap.put("/logout", "logout");
        fMap.put("/**", "user");

        shiroFilterFactoryBean.setLoginUrl("/gologin");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/goerror");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap);
        return shiroFilterFactoryBean;
    }

    //创建DefaultWebSecurityManager
    @Bean(name="defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm,@Qualifier("sessionManager")SessionManager sessionManager,@Qualifier("rememberMeManager")CookieRememberMeManager cookieRememberMeManager){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);//设置自定义realm
        defaultWebSecurityManager.setSessionManager(sessionManager);//设置session管理器
        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);//设置记住我功能
        return defaultWebSecurityManager;

    }

    //设置shiro的session配置
    @Bean(name = "sessionManager")
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdCookieEnabled(true);
        return sessionManager;
    }

    //凭证匹配器
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    //自定义realm
    @Bean(name = "userRealm")
    public UserRealm getRealm(@Qualifier("hashedCredentialsMatcher")HashedCredentialsMatcher hashedCredentialsMatcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher);//把加密算法的凭证器设置进realm
        return userRealm;
    }

    //开启shiro aop注解支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
        return authorizationAttributeSourceAdvisor;
    }
    //不加这个注解不生效,具体不详
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }
    //生命周期管理(可以不管)
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean(name = "rememberMeCookie")
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //如果httyOnly设置为true,则客户端不会暴露给客户端脚本代码,使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击;
        simpleCookie.setHttpOnly(true);
        //记住我cookie生效时间,单位是秒
        simpleCookie.setMaxAge(600);
        return simpleCookie;
    }

    //cookie管理器
    @Bean(name = "rememberMeManager")
    public CookieRememberMeManager rememberMeManager(@Qualifier("rememberMeCookie")SimpleCookie simpleCookie) {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        //rememberme cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位),通过以下代码可以获取
        //KeyGenerator keygen = KeyGenerator.getInstance("AES");
        //SecretKey deskey = keygen.generateKey();
        //System.out.println(Base64.encodeToString(deskey.getEncoded()));
        byte[] cipherKey = Base64.decode("wGiHplamyXlVB11UXWol8g==");
        cookieRememberMeManager.setCipherKey(cipherKey);
        cookieRememberMeManager.setCookie(simpleCookie);
        return cookieRememberMeManager;
    }
    //异常处理配置
    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "/goerror");//数据库异常处理
        mappings.setProperty("UnauthorizedException","/goerror");//授权异常
        mappings.setProperty("UnauthenticatedException","/goerror");//认证异常
        r.setExceptionMappings(mappings);  // None by default

        return r;
    }
}

6.controller

import com.lyh.shiro_springboot.dao.UserMapper;
import com.lyh.shiro_springboot.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class shiroController {
    @Autowired
    UserMapper userMapper;
    @RequestMapping("test")
    @RequiresRoles("admin")
    public ModelAndView testThyleaf(){
        List<Map<String,String>> lst = new ArrayList<>();
        Map<String,String> map1=new HashMap<>();
        map1.put("key1","value1");
        map1.put("key2","value2");
        map1.put("key3","value3");
        Map<String,String> map2=new HashMap<>();
        map2.put("key1","value1");
        map2.put("key2","value2");
        map2.put("key3","value3");
        Map<String,String> map3=new HashMap<>();
        map3.put("key1","value1");
        map3.put("key2","value2");
        map3.put("key3","value3");
        lst.add(map1);
        lst.add(map2);
        lst.add(map3);
        ModelAndView modelAndView = new ModelAndView("index");
        modelAndView.addObject("list",lst);
        return modelAndView;
    }
    @RequestMapping("gologin")
    public ModelAndView gologin(){
        return new ModelAndView("login");
    }

    @RequestMapping("tologin")
    public ModelAndView tologin(String userName, String passwd, String rememberMe){
        ModelAndView modelAndView = new ModelAndView();
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, passwd);
        if (rememberMe != null){
            if (rememberMe.equals("on")) {
                //说明选择了记住我
                token.setRememberMe(true);
            } else {
                token.setRememberMe(false);
            }
        }else{
            token.setRememberMe(false);
        }
        try {
            subject.login(token);
        } catch (Exception e) {
            modelAndView.addObject("msg", "用户名或密码错误!");
            modelAndView.setViewName("login");
            return modelAndView;
        }
        User user = userMapper.getUserByName(userName);
        Session session = subject.getSession();
        session.setAttribute("user",user);
        modelAndView.setViewName("index");
        return modelAndView;
    }

    @RequestMapping("goerror")
    public ModelAndView error(){
        return new ModelAndView("error");
    }

    @RequestMapping("logout")
    public ModelAndView logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return gologin();
    }
}

7.shiro集成ehcache缓存

    <dependency>
         <groupId>org.apache.shiro</groupId>
         <artifactId>shiro-all</artifactId>
         <version>1.4.0</version>
     </dependency>

上述shiro-all的pom文件里面已经包含shiro支持ehcache和ehcache本身的所有jar包

创建ehcache.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="java.io.tmpdir"/>
    
    <defaultCache
            eternal="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />
</ehcache>

<!--ehcache.xml 文件配置详解-->
<!--部分资料来源于网络-->
<!--diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。-->
<!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。-->
<!--name:缓存名称。-->
<!--maxElementsInMemory:缓存最大数目-->
<!--maxElementsOnDisk:硬盘最大缓存个数。-->
<!--eternal:对象是否永久有效,一但设置了,timeout将不起作用。-->
<!--overflowToDisk:是否保存到磁盘,当系统当机时-->
<!--timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。-->
<!--timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。-->
<!--diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。-->
<!--diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。-->
<!--memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。-->
<!--clearOnFlush:内存数量最大时是否清除。-->
<!--memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。-->
<!--FIFO,first in first out,先进先出-->
<!--LFU, Less Frequently Used,一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 -->
<!--LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。-->

编写shiro的配置类

    //创建DefaultWebSecurityManager
    @Bean(name="defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm,@Qualifier("sessionManager")SessionManager sessionManager,@Qualifier("rememberMeManager")CookieRememberMeManager cookieRememberMeManager){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);//设置自定义realm
        defaultWebSecurityManager.setSessionManager(sessionManager);//设置session管理器
        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);//设置记住我功能
        defaultWebSecurityManager.setCacheManager(ehCacheManager());//设置缓存
        return defaultWebSecurityManager;
    }
    //ehcache缓存管理器
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

8.解决通过记住我方式登录后没有session的问题

解决办法我们自己写一个拦截器,验证当通过记住我方式登录后,把session加进去.

SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer

import com.lyh.shiro_springboot.interceptor.RememberMeInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfg implements WebMvcConfigurer {
    /**
     * @param registry 添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(new RememberMeInterceptor());
        //排除配置
        interceptorRegistration.excludePathPatterns("/gologin");
        interceptorRegistration.excludePathPatterns("/tologin");
        interceptorRegistration.excludePathPatterns("/goerror");
        interceptorRegistration.excludePathPatterns("/logout");
        //配置拦截策略
        interceptorRegistration.addPathPatterns("/**");
    }
}

定义拦截器 implements HandlerInterceptor

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lyh.shiro_springboot.dao.UserMapper;
import com.lyh.shiro_springboot.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 拦截rememberMe的请求,添加user到session中
 * @author echo
 *
 */
public class RememberMeInterceptor implements HandlerInterceptor {
    @Autowired
    UserMapper userMapper;

    public RememberMeInterceptor() {
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("测试方法进入preHandle");
        // 获取session中的subject
        Subject subject = SecurityUtils.getSubject();

        // 判断是不是通过记住我登录
        if( !subject.isAuthenticated() && subject.isRemembered()) {
            Session session = subject.getSession();
            User user= (User)subject.getPrincipal();

            session.setAttribute("user",user);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub

    }
}

注:

三个重载方法说明

preHandle:调用Controller某个方法之前

postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法

afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理

相关文章

网友评论

      本文标题:9.shiro整合springboot

      本文链接:https://www.haomeiwen.com/subject/moihkctx.html