SpringBoot+thymeleaf+shiro整合小总结

首先确保在不进行shiro整合时正常登录以及拥有权限,然后我们再开始,以下是我个人的思路。
首先,准备依赖(这里只讲关键依赖,目前我就用到了这些,够用就行,也别把相关依赖全部弄进来):

<dependency><groupId>org.apache.shirogroupId><artifactId>shiro-springartifactId><version>1.3.2version>dependency><dependency><groupId>com.github.theborakompanionigroupId><artifactId>thymeleaf-extras-shiroartifactId><version>2.0.0version>dependency><dependency><groupId>org.apache.shirogroupId><artifactId>shiro-ehcacheartifactId><version>1.4.0version>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-thymeleafartifactId>dependency>

我的有关shiro整合的结构图:

主要了解的类是MyRealm和MyShiroConfig,在配置的时候参考了jiankang66的springboot整合shiro实现权限控制这篇(链接地址:https://blog.csdn.net/jiankang66/article/details/90473517)
首先MyShiroConfig的配置:

package team.glh.springboot.plant_manager.config;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
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.boot.autoconfigure.cache.CacheProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import team.glh.springboot.plant_manager.shiro.CustomSessionManager;
import team.glh.springboot.plant_manager.shiro.MyMatcher;
import team.glh.springboot.plant_manager.shiro.MyRealm;
import team.glh.springboot.plant_manager.shiro.MyRememberFilter;import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;@Configuration
public class MyShiroConfig {//密码验证器@Bean("credentialsMatcher")public CredentialsMatcher credentialsMatcher() {return new MyMatcher();}//权限验证器@Bean("myRealm")public MyRealm myRealm(@Qualifier("credentialsMatcher") CredentialsMatcher credentialsMatcher) {MyRealm myRealm = new MyRealm();//给权限验证器配置上自定义的密码验证器myRealm.setCredentialsMatcher(credentialsMatcher);return myRealm;}@Beanpublic CacheManager cacheManager(){return new MemoryConstrainedCacheManager();}/*** cookie对象;* rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cookie的有效时间等等。** @return*/@Beanpublic SimpleCookie rememberMeCookie() {
//        这个参数是cookie的名称,对应前端的checkbox的name=rememberMeSimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//        cookie生效时间为10秒simpleCookie.setMaxAge(10);return simpleCookie;}/*** cookie管理对象;* rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中** @return*/@Beanpublic CookieRememberMeManager rememberMeManager() {CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());return  cookieRememberMeManager;}@Beanpublic MyRememberFilter MyRememberFilter(){return new MyRememberFilter();}/*** 自定义sessionManager* @return*/@Beanpublic SessionManager sessionManager(){return new CustomSessionManager();}//桥梁,主要是Realm的管理认证配置@Bean("securityManager")public SecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm) {DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();//注入自定义myRealmdefaultWebSecurityManager.setRealm(myRealm);//注入自定义cacheManagerdefaultWebSecurityManager.setCacheManager(cacheManager());//注入记住我管理器defaultWebSecurityManager.setRememberMeManager(rememberMeManager());//注入自定义sessionManagerdefaultWebSecurityManager.setSessionManager(sessionManager());//自定义缓存实现,使用redis
//        defaultWebSecurityManager.setSessionManager(SessionManager());return defaultWebSecurityManager;}//进行全局配置,Filter工厂,设置对应的过滤条件和跳转条件@Bean("shiroFilterFactoryBean")public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager securityManager) {//shiro对象ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();bean.setSecurityManager(securityManager);bean.setLoginUrl("/toLogin");bean.setSuccessUrl("/success");Map<String, Filter> filterMap=new LinkedHashMap<String,Filter>();filterMap.put("MyRememberFilter",MyRememberFilter());/* //自定义拦截器Map filterMap=new LinkedHashMap();//限制同一账号同时在线的个数
//        filterMap.put("kickout",kickoutSessionControlFilter());bean.setFilters(filterMap);*///MAPLinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<String, String>();/*认证顺序是从上往下执行。*/linkedHashMap.put("/logout", "logout");//在这儿配置登出地址,不需要专门去写控制器。linkedHashMap.put("/static/**", "anon");//开启注册页面不需要权限
//        linkedHashMap.put("/register", "anon");
//        linkedHashMap.put("/saveregister", "anon");//验证phone唯一
//        linkedHashMap.put("/solephone", "anon");//获取验证码
//        linkedHashMap.put("/getcode", "anon");//验证码判断
//        linkedHashMap.put("/comparecode", "anon");
//        linkedHashMap.put("/websocket", "anon");//必须开启。
//        linkedHashMap.put("/css/**", "anon");//不需要验证
//        linkedHashMap.put("/js/**", "anon");//不需要验证//配置错误页面linkedHashMap.put("error", "anon");//不需要验证linkedHashMap.put("/avatars/**", "anon");//不需要验证linkedHashMap.put("/css/**", "anon");//不需要验证linkedHashMap.put("/font/**", "anon");//不需要验证linkedHashMap.put("/images/**", "anon");//不需要验证linkedHashMap.put("/js/**", "anon");//不需要验证linkedHashMap.put("/login/**", "anon");//不需要验证linkedHashMap.put("/**", "user");//需要进行权限验bean.setFilterChainDefinitionMap(linkedHashMap);return bean;}@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}//加入·注解的使用,不加入这个注解不生效//启Shiro的注解(@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证//     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能@Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();sourceAdvisor.setSecurityManager(securityManager);return sourceAdvisor;}//    进行权限认证的,没有会使得前台的shiro标签无法使用//shiro结合thymeleaf实现细粒度权限控制@Beanpublic ShiroDialect shiroDialect() {return new ShiroDialect();}}

MyRealm

package team.glh.springboot.plant_manager.shiro;import com.alibaba.fastjson.JSON;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import team.glh.springboot.plant_manager.entity.sys.SysPermission;
import team.glh.springboot.plant_manager.entity.sys.SysRole;
import team.glh.springboot.plant_manager.entity.sys.SysUser;
import team.glh.springboot.plant_manager.service.itf.sys.ISysPermissionService;
import team.glh.springboot.plant_manager.service.itf.sys.ISysRoleService;
import team.glh.springboot.plant_manager.service.itf.sys.ISysUserService;import java.util.HashSet;
import java.util.List;
import java.util.Set;public class MyRealm extends AuthorizingRealm {private Logger logger= LoggerFactory.getLogger(this.getClass());@Autowiredprivate ISysUserService sysUserService;@Autowiredprivate ISysRoleService roleService;@Autowiredprivate ISysPermissionService sysPermissionService;/*** 权限验证* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();SysUser user = sysUserService.listByAccount(principals.toString());if (null != user) {SysRole role = roleService.getById(user.getRoleId());if (null != role) {List<SysPermission> permissionList = sysPermissionService.listByRoleId(role.getId());logger.info("当前用户拥有的权限:"+JSON.toJSONString(permissionList));Set<String> roles = new HashSet<>();Set<String> permissions = new HashSet<>();roles.add(role.getCode());if (null != permissionList) {for (SysPermission permission : permissionList) {permissions.add(permission.getCode());}}logger.info("拥有的角色:"+role.getCode()+"拥有的权限"+ JSON.toJSONString(permissions));simpleAuthorizationInfo.addRoles(roles);simpleAuthorizationInfo.addStringPermissions(permissions);}simpleAuthorizationInfo.addStringPermission("user");}return simpleAuthorizationInfo;}/*** 用户身份验证* @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//从token获取用户名,从主体传过来的认证信息中获取//加这一步的目的是在post请求时会先进入认证然后再到请求。if(authenticationToken.getPrincipal()==null){return null;}//获取用户的登录信息,用户名String account=authenticationToken.getPrincipal().toString();//根据service调用用户名,查找用户的全部信息//通过用户名到数据库获取凭证SysUser sysUser = sysUserService.listByAccount(account);if (null != sysUser) {String password = sysUser.getPassword();Integer state = sysUser.getState();// 1:正常 2:禁用 3:锁定if (state == (short)2) {throw new DisabledAccountException("禁用账号");} else if (state == (short)3) {throw new LockedAccountException("密码输入错误次数大于5,账号锁定");} else {return new SimpleAuthenticationInfo(account, password,getName());}} else {throw new UnknownAccountException("账户不存在");}}}

CustomSessionManager

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;public class CustomSessionManager extends DefaultWebSessionManager {}

MyMatcher

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.crypto.hash.Sha384Hash;/*** @Author:* @Date: Created in 下午5:32 2020/2/11* @todo:验证密码 查找到了该用户 自定义密码验证器*/
public class MyMatcher extends SimpleCredentialsMatcher {@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;String pwd = encrypt(String.valueOf(usernamePasswordToken.getPassword()));String mysqlpwd = (String) info.getCredentials();return this.equals(pwd, mysqlpwd);}//将传进来的密码进行加密的方法private String encrypt(String data){String sha384Hex=new Sha384Hash(data).toBase64();return sha384Hex;}}

MyRemenberFilter

package team.glh.springboot.plant_manager.shiro;import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @Author: Gan LingHao* @Date: Created in 下午6:32 2020/2/11* @todo: 暂时未启用*/
public class MyRememberFilter extends FormAuthenticationFilter {protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, Object mappedValue){Subject subject=getSubject(request,response);if(!subject.isAuthenticated() && subject.isRemembered()){if(subject.getSession().getAttribute("user")==null &&subject.getPrincipal()!=null){subject.getSession().setAttribute("user",subject.getPrincipal());}}return subject.isAuthenticated() || subject.isRemembered();}
}

ShiroUtil

package team.glh.springboot.plant_manager.shiro;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import team.glh.springboot.plant_manager.entity.sys.SysUser;
import team.glh.springboot.plant_manager.vo.Contants;
import team.glh.springboot.plant_manager.vo.SysPermissionVO;import java.util.List;
import java.util.ResourceBundle;/*** @Author: * @Date: Created in 下午10:47 2019/12/30* @todo: 加密密码*/
public class ShiroUtil {private static Logger logger= LoggerFactory.getLogger(ShiroUtil.class);private static String TYPE;private static Integer COUNT;static {ResourceBundle resourceBundle=ResourceBundle.getBundle("realm");TYPE=resourceBundle.getString("realm.hashed.type");COUNT= Integer.parseInt(resourceBundle.getString("realm.hashed.count"));}/*** 密码加密* @param account* @param password* @return*/public static String saltPwd(String account,String password){ByteSource salt= ByteSource.Util.bytes(account);return new SimpleHash(TYPE,password,salt,COUNT).toString();}/*** 当前操作用户保存到session* @param user*/public static void setCurrentUser(SysUser user){Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();session.setAttribute(Contants.LOGIN_USER,user);}/*** 获取当前登录成功的用户* @return*/public static SysUser getCurrentLoginUser(){Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();SysUser user = null != session.getAttribute(Contants.LOGIN_USER)?(SysUser)session.getAttribute(Contants.LOGIN_USER):null;return user;}/*** 当用户退出系统或登录失败时移除当前操作用户*/public static void removeCurrentUser(){Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();session.removeAttribute(Contants.LOGIN_USER);logger.info("当前sessionId "+session.getId());}/*** 将获取到的有效权限放到session中* @param allPermission*/public static void setAllPermission(List<SysPermissionVO> allPermission){Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();session.setAttribute(Contants.ALL_PERMISSION,allPermission);session.touch();}/*** 移除之前session中获取到的有效权限*/public static void removeAllPermission(){Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();session.removeAttribute(Contants.ALL_PERMISSION);}/*** 重新赋值权限(在比如:给一个角色临时添加一个权限,需要调用此方法刷新权限,否则还是没有刚赋值的权限)* @param account 当前登录用户的用户名*/public static void reloadAuthorizing(String account){//重新修改权限后清楚缓存,调用doGetAuthorizationInfo重新取角色的权限信息RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();MyRealm myRealm = (MyRealm) rsm.getRealms().iterator().next();Subject subject = SecurityUtils.getSubject();String realmName = subject.getPrincipals().getRealmNames().iterator().next();//shiroRealm.clearAllCachedAuthorizationInfo2();//清楚所有用户权限//第一个参数为用户名,第二个参数为realmName,test想要操作权限的用户SimplePrincipalCollection principals = new SimplePrincipalCollection(account,realmName);subject.runAs(principals);myRealm.getAuthorizationCache().remove(subject.getPrincipals());subject.releaseRunAs();}}

LoginController

package team.glh.springboot.plant_manager.controller.basic;import org.apache.ibatis.annotations.Param;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import team.glh.springboot.plant_manager.entity.sys.SysUser;
import team.glh.springboot.plant_manager.service.itf.sys.ISysPermissionService;
import team.glh.springboot.plant_manager.service.itf.sys.ISysRoleService;
import team.glh.springboot.plant_manager.service.itf.sys.ISysUserService;
import team.glh.springboot.plant_manager.shiro.ShiroUtil;
import team.glh.springboot.plant_manager.vo.ResponseMessage;
import team.glh.springboot.plant_manager.vo.SysPermissionVO;import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;/*** @Author: * @Date: Created in 下午12:32 2020/2/8* @todo: 登录*/
@Controller
public class LoginController {private Logger logger= LoggerFactory.getLogger(getClass());@Autowiredprivate ISysUserService sysUserService;@Autowiredprivate ISysRoleService roleService;@Autowiredprivate ISysPermissionService sysPermissionService;@RequestMapping("/toLogin")public String toLogin(){return "basic/login";}/*** post登录** @param account* @param password* @return*/@RequestMapping(value = "/login", method = RequestMethod.POST)public String login2(@Param("account") String account, @Param("password") String password,Model model,HttpServletRequest request) {Subject subject= SecurityUtils.getSubject();UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(account,password);ResponseMessage msg=new ResponseMessage();try{subject.login(usernamePasswordToken);if(subject.isAuthenticated()){logger.info("登录成功");String host=request.getRemoteHost()+":"+request.getRemotePort();logger.info("当前登录host:port"+host);SysUser sysUser=sysUserService.listByAccount(account);sysUser.setLastLoginTime(sysUser.getThisLoginTime());sysUser.setLastLoginIp(sysUser.getThisLoginIp());sysUser.setThisLoginTime(LocalDateTime.now());sysUser.setThisLoginIp(host);//将用户信息存入session中ShiroUtil.setCurrentUser(sysUser);sysUserService.saveOrUpdate(sysUser);return "redirect:success";}else{msg.setValue(110);msg.setDesc("认证失败");logger.info("认证失败");model.addAttribute("msg",msg);return "basic/login";}}catch (UnknownAccountException ex){msg.setValue(110);msg.setDesc("账号不存在");logger.info("账号不存在");model.addAttribute("msg",msg);return "basic/login";} catch (IncorrectCredentialsException ex){msg.setValue(110);msg.setDesc("密码错误");logger.info("密码错误");model.addAttribute("msg",msg);return "basic/login";} catch (LockedAccountException ex){msg.setValue(110);msg.setDesc("错误次数过多,锁定账号");logger.info("错误次数过多,锁定账号");model.addAttribute("msg",msg);return "basic/login";} catch (DisabledAccountException ex){msg.setValue(110);msg.setDesc("此帐号已被禁用");logger.info("此帐号已被禁用");model.addAttribute("msg",msg);return "basic/login";}}@RequestMapping("/success")public String success(Model model){List<SysPermissionVO> permissions=sysPermissionService.listByEffective(1,1);ShiroUtil.setAllPermission(permissions);model.addAttribute("index","index");return "basic/index";}@RequestMapping("/logout")public String logout(){logger.info(ShiroUtil.getCurrentLoginUser().getName()+"退出系统");//        SecurityUtils.getSubject().logout();ShiroUtil.removeCurrentUser();ShiroUtil.removeAllPermission();return "basic/login";}}

前端关于shiro的代码

               <lith:each="permission:${session.ALL_PERMISSION}" class=""th:class="${permission.code == parentCode}?'active open'">
<a href="#" class="dropdown-toggle" shiro:hasPermission="${permission.code}"><i class="icon-list">i><span class="menu-text"> [[${permission.name}]]span><b class="arrow icon-angle-down">b>a><ul class="submenu"><li th:each="children:${permission.childrens}"th:class="${children.code == sonCode}?'active'">
<a href="tables.html" th:href="@{${children.url}}" shiro:hasPermission="${children.code}"><i class="icon-double-angle-right">i>[[${children.name}]]a>
li>ul>
li>

要想开启提示,导入它的命名空间

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

这就是有关整合的全部有效代码,貌似不缺。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部