SpringBoot self (v) integration shiro

Disclaimer: This article is a blogger original article, please respect the original, without bloggers allow prohibited reproduced, reserved the right to pursue https://blog.csdn.net/qq_29914837/article/details/90182787

A, pom.xml

Shiro rely on the use of all, because I use thymeleaf module, so only thymeleaf + shiro dependent, if other front-end template, you need to import other dependent

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<commons-pool2.version>2.6.1</commons-pool2.version>
		<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
		<thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version>

	</properties>

<!-- shiro+redis缓存插件 -->
		<dependency>
			<groupId>org.crazycake</groupId>
			<artifactId>shiro-redis</artifactId>
			<version>2.4.2.1-RELEASE</version>
			<exclusions>
				<exclusion>
					<artifactId>shiro-core</artifactId>
					<groupId>org.apache.shiro</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- shiro 依赖 -->

		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.3.2</version>
		</dependency>

		<!--thymeleaf- shiro -->
		<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>${thymeleaf-layout-dialect.version}</version>
		</dependency>

Second, the structure of the database access control settings

Here Insert Picture Description
I have here the classic design using permissions Table 5
user table
role table
users - roles table
privileges resource table
Roles - Permissions table

5 table basically complete function right, of course, is based on the need to design their own projects, but where the relationship still so!

User table t_sys_user
no introduction, mainly basic user information, user_state user state, such as enabling, lock, disabled and other state
Here Insert Picture Description

Roles table t_sys_role

role_state the role of the state, role_sort sort
Here Insert Picture Description

User - role relationship table (t_sys_user_role) intermediate table

user_id and role_id are user id and the role id, saving the user with information role

Here Insert Picture Description
Permissions resource table (t_sys_permission)

Here we are talking about major conservation authority, and generally will be divided into several types, such as menu rights, permissions, and privileges for the button
Here Insert Picture Description

I'm here mainly to control user access permissions have been a number of menu buttons such as url (view, edit, delete) permissions, etc.
Here Insert Picture Description

Roles - Permissions resource table (t_sys_role_permission)

I am here mainly controlled by role permissions, assign different roles to users, assign roles to have authority, then equivalent to assign permissions to users, and management is also more convenient
Here Insert Picture Description

Third, the shiro integrate in a positive springboot

After the business presentation clear, complete the corresponding function code
Here Insert Picture Description
part of the code I have not presented here, such as the establishment of the entity class

springboot integration shiro
corresponding to spring consolidation shiro, a lot easier, do not need too complex configuration and integration springboot shiro, pattern is also more standardized, can be configured in accordance with specifications.
Here Insert Picture Description
Configure Some application.yml

spring:
      
  #thymeleaf模块配置
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    ##非严格检查-HTML
    ##严格检查-HTML5
    mode:  HTML
    encoding: UTF-8 
    #清除缓存,实现热部署。也就是修改了html后不用重启,刷新页面就能看到效果。再回到浏览器刷新,就能看到效果了     
    cache: false  
  #数据库配置
  datasource:
    url: jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name:  com.mysql.cj.jdbc.Driver
  jpa:
    #禁用视图
    open-in-view: false

  #单个redis配置  
  redis:
    #Redis服务器地址
    host: 127.0.0.1
    #Redis服务器连接端口
    port: 6379
    #Redis服务器连接密码(默认为空)
    password:
    # Redis数据库索引(默认为0)
    database: 0
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-active: 32
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-wait: -1
        # 连接池中的最大空闲连接 默认 8
        max-idle: 8
        # 连接池中的最小空闲连接 默认 0
        min-idle: 0
        # 连接超时时间(毫秒)
        timeout: 0


    

  

ShiroConfig shiro is the core of the filter
is indispensable, shiro provides many functions, which is shiro advantage, without too much relationship between business functions, as long as the framework in accordance with specifications provided by shiro.

package com.springboot.main.core.config.shiro;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
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.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.crazycake.shiro.RedisCacheManager;
import com.springboot.main.eimm.permission.entity.Permission;
import com.springboot.main.eimm.permission.service.PermissionService;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import lombok.extern.slf4j.Slf4j;

/**
 * @method shiro核心配置
 * @author Mr yi
 * @time 2019年5月6日
 */
@Configuration
@Slf4j
public class ShiroConfig {

	@Autowired(required = false)
	private PermissionService permissionService;

	@Value("${spring.redis.host}")
	private String host;

	@Value("${spring.redis.port}")
	private int port;

	@Value("${spring.redis.lettuce.pool.timeout}")
	private int timeout;

	@Value("${spring.redis.password}")
	private String password;

	@Bean
	public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	
	
    
	/**
	 * @method ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	@Bean("shiroDialect")
	public ShiroDialect shiroDialect() {
		log.info("配置thymeleaf方言shiroDialect");
		return new ShiroDialect();
	}
	/**
     * 
     * @param
     * @return org.springframework.beans.factory.config.MethodInvokingFactoryBean
     * @author Zain
     * @description Spring静态注入:让某个方法的返回值注入bean实例
     * @date 2019/1/9 17:18
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(SecurityManager securityManager){
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(securityManager);
        return bean;
    }
    @SuppressWarnings("rawtypes")
	@Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }
	
	/**
	 * 
	 * @method ShiroFilterFactoryBean 处理拦截资源文件问题。
	 *         注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
	 *         初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager Filter Chain定义说明
	 *         1、一个URL可以配置多个Filter,使用逗号分隔
	 *         2、当设置多个过滤器时,全部验证通过,才视为通过3、部分过滤器可指定参数,如perms,roles
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @param securityManager
	 * @return
	 */
	@Bean("shiroFilter")
	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
		log.info("ShiroConfiguration.shirFilter()");
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/main/login");
		// 登录成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/main/main/index");
		// 未授权界面;
		shiroFilterFactoryBean.setUnauthorizedUrl("/main/error/error-404");
		// 拦截器.
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

		filterChainDefinitionMap.put("/", "user");
		filterChainDefinitionMap.put("/main/main/index", "user");
		
		// 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,这里我们在MainController自定义退出方法
		filterChainDefinitionMap.put("/**","anon");
		
		/* filterChainDefinitionMap.put("/main/logout", "logout"); */
		/*
		 * filterChainDefinitionMap.put("/css/**","anon");
		 * filterChainDefinitionMap.put("/js/**","anon");
		 * filterChainDefinitionMap.put("/img/**","anon");
		 * filterChainDefinitionMap.put("/font-awesome/**","anon");
		 */
		// <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
		// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
		// 自定义加载权限资源关系
		List<Permission> permissionList = permissionService.selectByMap(new HashMap<String, Object>());
		for (Permission permission : permissionList) {
			if (StringUtils.isNotEmpty(permission.getPermission_url())) {
				String url = "perms[" + permission.getPermission_url() + "]";
				filterChainDefinitionMap.put(permission.getPermission_url(), url);
			}
		}
		filterChainDefinitionMap.put("/**", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	
	//配置核心安全事务管理器
    @Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		// 设置realm.
		securityManager.setRealm(myShiroRealm());
		// 自定义缓存实现 使用redis
		securityManager.setCacheManager(cacheManager());
		// 自定义session管理 使用redis
		securityManager.setSessionManager(sessionManager());
		//注入记住我管理器;
        securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}
	/**
	 * @method MyShiroRealm自定义Realm-配置自定义的权限登录器
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	/**
	 * @method 凭证匹配器
	 *         (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了,所以我们需要修改下doGetAuthenticationInfo中的代码;
	 *         )
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于 md5(md5(""));
		return hashedCredentialsMatcher;
	}

	/**
	 * @method 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * @method 配置shiro redisManager 使用的是shiro-redis开源插件
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	public RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost(host);
		redisManager.setPort(port);
		redisManager.setExpire(1800);// 配置缓存过期时间
		redisManager.setTimeout(timeout);
		redisManager.setPassword(password);
		return redisManager;
	}

	/**
	 * @method cacheManager 缓存 redis实现 使用的是shiro-redis开源插件
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	public RedisCacheManager cacheManager() {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setRedisManager(redisManager());
		return redisCacheManager;
	}

	/**
	 * @method RedisSessionDAO shiro sessionDao层的实现 通过redis 使用的是shiro-redis开源插件
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	@Bean
	public RedisSessionDAO redisSessionDAO() {
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setRedisManager(redisManager());
		return redisSessionDAO;
	}

	/**
	 * @method shiro session的管理-使用的是shiro-redis开源插件
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	@Bean
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(redisSessionDAO());
		return sessionManager;
	}
	
	/**
     * cookie对象;
     *
     * @return
     */
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间7天 ,单位秒;-->
        simpleCookie.setMaxAge(604800);
        return simpleCookie;
    }
    
    /**
     * cookie管理对象;记住我功能
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

}

MyShiroRealm shiro Custom Realm, shiro more central points: authentication, authorization.
It will be executed when the user login authentication method, only authenticated users can access the system;
it is authorized to acquire user rights data, in order to determine whether the user has permission to access this resource!

I use redis cache to achieve, the official recommended EhCache

package com.springboot.main.core.config.shiro;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import com.springboot.main.eimm.permission.entity.Permission;
import com.springboot.main.eimm.permission.service.PermissionService;
import com.springboot.main.eimm.user.entity.User;
import com.springboot.main.eimm.user.service.UserService;
import javax.annotation.Resource;
import java.util.*;

/**
 * @method shiro自定义Realm
 * @author Mr yi
 * @time 2019年5月6日
 */
public class MyShiroRealm extends AuthorizingRealm {

	@Resource
	private UserService userService;

	@Resource
	private PermissionService permissionService;

	@Autowired
	private RedisSessionDAO redisSessionDAO;

	// 授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		User user = (User) SecurityUtils.getSubject().getPrincipal(); 
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("role_state", "001");//角色状态:启用
		map.put("user_id", user.getId());//用户id
		map.put("permission_state", "001");//权限状态:启用
		map.put("permission_type", "001"); //权限类型:菜单
		List<Permission> permissionList = permissionService.getPermissionListByUserId(map);
		// 获取该用户具有的全部权限
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		for (Permission permission : permissionList) {
			String url = permission.getPermission_url();
			//权限不能有空数据
			if(StringUtils.isNotBlank(url))
			info.addStringPermission(permission.getPermission_url());
		}
		return info;
	}

	// 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 获取用户的输入的账号.
		String userName = (String) token.getPrincipal();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("user_name", userName);
		User user = userService.selectUserByUserName(map);

		if (user == null || StringUtils.isBlank(userName)) {
			throw new UnknownAccountException();
		}
		if (StringUtils.equals("002", user.getUser_state())) { // 锁定
			throw new LockedAccountException(); // 帐号锁定
		}
		if (StringUtils.equals("003", user.getUser_state())) { // 禁用
			throw new DisabledAccountException("该账户已被禁用!");
		}
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, // 用户
				user.getUser_pwd(), // 密码
				ByteSource.Util.bytes(userName), getName() // realm name
		);
		//当验证都通过后,把用户信息放在session里
		 Session session = SecurityUtils.getSubject().getSession();
		 session.setAttribute("userSession", user);
		 session.setAttribute("userSessionId", user.getId());
		 
		return authenticationInfo;
	}

	/**
	 * @method 根据userId 清除当前session存在的用户的权限缓存(在shiro中权限清空后会重新调用授权方法)
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @param userIds
	 */
	public void clearUserAuthByUserId(List<String> userIds) {
		if (null == userIds || userIds.size() == 0)
			return;
		// 获取所有session
		Collection<Session> sessions = redisSessionDAO.getActiveSessions();
		// 定义返回
		List<SimplePrincipalCollection> list = new ArrayList<SimplePrincipalCollection>();
		for (Session session : sessions) {
			// 获取session登录信息。
			Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
			if (null != obj && obj instanceof SimplePrincipalCollection) {
				// 强转
				SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
				// 判断用户,匹配用户ID。
				obj = spc.getPrimaryPrincipal();
				if (null != obj && obj instanceof User) {
					User user = (User) obj;
					// 比较用户ID,符合即加入集合
					if (null != user && userIds.contains(user.getId())) {
						list.add(spc);
					}
				}
			}
		}
		RealmSecurityManager securityManager = (RealmSecurityManager) SecurityUtils.getSecurityManager();
		MyShiroRealm realm = (MyShiroRealm) securityManager.getRealms().iterator().next();
		for (SimplePrincipalCollection simplePrincipalCollection : list) {
			realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
		}
	}
}

Authenticated users access the system will automatically block, jump to the login screen login
to submit login information to log into the login method

package com.springboot.main.eimm.main.controller;

import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.springboot.main.eimm.user.entity.User;
import lombok.extern.slf4j.Slf4j;

/**
 * @method 程序主程序控制层方法(登录注销)
 * @author Mr yi
 * @time 2019年5月6日
 */
@Controller
@Slf4j
@RequestMapping("main")
public class MainController {

	/**
	 * 
	 * @method 通用访问页面方法(访问方式:http://127.0.0.1:8080/spring-main/main/main/content) 访问main文件夹下的content。html页面
	 * @author Mr yi
	 * @time 2019年5月9日
	 * @param module
	 * @param usecase
	 * @return
	 */
	@RequestMapping("/{module}/{usecase}")
	public String index(@PathVariable("module")String module,@PathVariable("usecase")String usecase){				
		return module+"/"+usecase;
	}
	
	/**
	 * @method 用户登录(get)
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @return
	 */
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String login() {
		return "login";
	}

	/**
	 * @method 用户登录(post)
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @param request
	 * @param user
	 * @param model
	 * @return
	 */
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String login(HttpServletRequest request, User user, Model model,boolean rememberMe) {
		if (StringUtils.isEmpty(user.getUser_name()) || StringUtils.isEmpty(user.getUser_pwd())) {
			request.setAttribute("msg", "账号或密码不能为空!");
			return "login";
		}
		
		UsernamePasswordToken token = new UsernamePasswordToken(user.getUser_name(), user.getUser_pwd(),rememberMe);
		//获取当前的Subject  
		Subject subject = SecurityUtils.getSubject();
		try {
			subject.login(token);
			return "redirect:main/index";
		} catch (LockedAccountException lae) {
			token.clear();
			request.setAttribute("msg", "该账号已被锁定!");
			return "login";
		} catch (DisabledAccountException e) {
			token.clear();
			request.setAttribute("msg", "该账号已被禁用!");
			return "login";
		} catch (AuthenticationException e) {
			token.clear();
			request.setAttribute("msg", "账号或密码不正确!");
			return "login";
		}
	}


	/**
	 * @method 注销(logout方法自动清空shiro相关用户缓存)
	 * @author Mr yi 
	 * @time 2019年5月6日
	 * @return
	 */
	@RequestMapping("/logout")
	public String logout1() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		log.info(" 用户注销成功 !");
		return "login";
	}
	
	
	
}

dao as a method mainly getPermissionListByUserId (), the user has permission to obtain permission, the point to note here, permission_url authority links can not be empty, in front of shiro authorized, the user needs to obtain permission to have. Permission must be requested to not be empty, if permission is empty, it will throw an error.

/**
	 * @method 查询用户具有的全部权限链接( permission_url 不为空的)
	 * @author Mr yi
	 * @time 2019年5月6日
	 * @param user_id
	 * @return
	 */
	@Select({"<script>",
		    	"SELECT DISTINCT t1.* from t_sys_permission t1 ",
		    	"INNER JOIN t_sys_role_permission t2 on t1.id=t2.permission_id",
		    	"INNER JOIN t_sys_role t3 on t2.role_id=t3.id",
		    	//角色状态判断
		    	"<when test='role_state!=null'>", 
					"and t3.role_state= #{role_state}", 
				"</when>",
				//用户id
		    	"INNER JOIN t_sys_user_role t4 on t3.id=t4.role_id and t4.user_id=#{user_id} where 1=1 ",
		    	//权限状态判断
		    	"<when test='permission_state!=null'>", 
					"and t1.permission_state= #{permission_state}", 
				"</when>",
				//权限类型判断
		    	"<when test='permission_type!=null'>", 
					"and t1.permission_type= #{permission_type}", 
				"</when>",
		    	"ORDER BY t1.permission_sort asc ",
			" </script>"})
	public List<Permission> getPermissionListByUserId(Map<String, Object> columnMap);

Four, thymeleaf label use shiro

shiro provide a tag library, introduced specific syntax

1, as to the pom.xml, mentioned earlier a

springboot4 above the current requirement 2.0.0 above

<!--thymeleaf- shiro -->
		<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>${thymeleaf-layout-dialect.version}</version>
		</dependency>

2, disposed in ShiroDialect in ShiroConfig

@Bean
public ShiroDialect shiroDialect() {
    return new ShiroDialect();
}

3, html tag introduced shiro

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

4, the use of labels shiro

<shiro:principal property=“user_name”/>

Fifth, use thymeleaf use shiro page throwing error problem

1, SpringBoot hot deployment lead to the same type conversion appears abnormal ClassCastException type conversion

This is because of the plug-hot deployment springboot caused should springboot and hot deployment of plug-ins are not the same unified classloader, class loader lead
Here Insert Picture Description
such as: authentication is successful, will check out the objects stored in the session,
then remove the object from the session is object the object conversion, reported java.lang.ClassCastException not the same object.

META_INF solution is to create a folder in the following resources directory, then create spring-devtools.properties file, add the following configuration similar

restart.include.shiro-redis=/shiro-[\\w-\\.]+jar

Hot or not applicable to deploy plug-ins

2, springboot thymleaf cause error in the tag resolution shiro

The reason: SpringBoot hot deployment resulting in
Here Insert Picture Description
NA can be used shiro tag to normal after spring-boot-devtools


If you think this article to help you, then click the Follow button troublesome picture on the right, thank you!

Technical progress in the exchange, dissemination of knowledge sharing in

Guess you like

Origin blog.csdn.net/qq_29914837/article/details/90182787