LayUI + Shiro 实现动态菜单并记住菜单收展
Posted Calvin Chan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LayUI + Shiro 实现动态菜单并记住菜单收展相关的知识,希望对你有一定的参考价值。
LayUI + Shiro 实现动态菜单 + 记住菜单收展
LayUI + Shiro + Thyemleaf 实现动态菜单并记住菜单收展
一、Maven 依赖
<dependencies>
<!--阿里 FastJson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.39</version>
</dependency>
<!--权限控制 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.4.0-RC2</version>
</dependency>
<!-- 兼容于thymeleaf的shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
二、菜单相关的类
1、主菜单
/**
* @author wxhntmy
*/
@Getter
@Setter
public class Menu {
private String name;
private String icon;
private String url;
private Boolean hidden;
private List<MenuList> list;
}
2、子菜单
/**
* @author wxhntmy
*/
@Getter
@Setter
public class MenuList {
private String name;
private String url;
public MenuList(String name, String url) {
this.name = name;
this.url = url;
}
}
三、Shiro 配置
1、ShiroConfig
/**
* @author wxhntmy
*/
@Configuration
public class ShiroConfig {
/**
* 配置拦截器
* <p>
* 定义拦截URL权限,优先级从上到下 1). anon : 匿名访问,无需登录 2). authc : 登录后才能访问 3). logout: 登出 4).
* roles : 角色过滤器
* <p>
* URL 匹配风格 1). ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/; 2).
* *:匹配零个或多个字符串,如 /admin* 将匹配 /admin 或/admin123,但不匹配 /admin/1; 2).
* **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b
* <p>
* 配置身份验证成功,失败的跳转路径
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 静态资源匿名访问
filterChainDefinitionMap.put("/layui/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/admin/**", "anon");
filterChainDefinitionMap.put("/**/*.eot", "anon");
filterChainDefinitionMap.put("/**/*.svg", "anon");
filterChainDefinitionMap.put("/**/*.svgz", "anon");
filterChainDefinitionMap.put("/**/*.ttf", "anon");
filterChainDefinitionMap.put("/**/*.woff", "anon");
filterChainDefinitionMap.put("/**/*.woff2", "anon");
filterChainDefinitionMap.put("/**/*.gif", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/menu", "anon");
filterChainDefinitionMap.put("/user/login", "anon");
// 用户退出
filterChainDefinitionMap.put("/logout", "logout");
// 其他路径均需要身份认证,一般位于最下面,优先级最低
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//登录路径
shiroFilterFactoryBean.setLoginUrl("/login");
// 主页
//shiroFilterFactoryBean.setSuccessUrl("/index");
//验证失败跳转的路径
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
return shiroFilterFactoryBean;
}
/**
* SecurityManager安全管理器;shiro的核心
*
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm());
return defaultWebSecurityManager;
}
/**
* 自定义Realm
*
* @return
*/
@Bean
public MyRealm myRealm() {
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(myCredentialsMatcher());
return myRealm;
}
/**
* 配置加密方式
* @return
*/
@Bean
public MyCredentialsMatcher myCredentialsMatcher() {
return new MyCredentialsMatcher();
}
/**
* 配置Shiro生命周期处理器
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 自动创建代理类,若不添加,Shiro的注解可能不会生效。
*/
@Bean
@DependsOn({ "lifecycleBeanPostProcessor" })
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启Shiro的注解
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
2、自定义shiro密码校验
/**
* 自定义shiro密码校验
* @author wxhntmy
*/
public class MyCredentialsMatcher implements CredentialsMatcher {
@Resource
private UserMapper userMapper;
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken utoken = (UsernamePasswordToken) token;
String password = new String(utoken.getPassword());
String username = utoken.getUsername();
User user = userMapper.getUserById(username);
return user.getPwd().equals(password);
}
}
3、MyRealm
/**
* @author wxhntmy
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private RoleMapper roleMapper;
@Resource
private UserRoleListMapper userRoleListMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User) principalCollection.getPrimaryPrincipal();
if (user == null) {
return null;
}
List<UserRoleList> roleLists = userRoleListMapper.getUserRoleByUserId(user.getId());
List<Role> roles = roleMapper.getAllRoles();
if (roleLists != null && !roleLists.isEmpty()) {
for (UserRoleList roleList : roleLists) {
for (Role role : roles) {
if (Objects.equals(roleList.getRole_id(), role.getId())) {
authorizationInfo.addRole(role.getRole());
}
}
}
}
return authorizationInfo;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取登录用户账号
UsernamePasswordToken utoken = (UsernamePasswordToken) authenticationToken;
//获得用户输入的密码
String password = new String(utoken.getPassword());
String username = utoken.getUsername();
User user = new User();
user.setId(username);
user.setPwd(password);
//当前realm对象的唯一名字,调用父类的getName()方法
String realmName = getName();
// 获取盐值,即用户名
ByteSource salt = ByteSource.Util.bytes(password);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, salt, realmName);
return info;
}
}
四、控制类
1、LoginController
@RestController
public class LoginController {
@Resource
private RoleMapper roleMapper;
@Resource
private UserRoleListMapper userRoleListMapper;
@Resource
private UserMapper userMapper;
@RequestMapping(value = "/user/login", method = RequestMethod.GET)
public Msg<String> getUserByName(@RequestParam String user,
@RequestParam String pwd,
@RequestParam String usertype,
@RequestParam String box) {
Role role = roleMapper.getRoleByRoleName(usertype);
User uUser = userMapper.getUserById(user);
if (uUser == null){
return Msg.fail("UserUnexit");
}
//登录验证
UsernamePasswordToken token = new UsernamePasswordToken(user, pwd);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (AuthenticationException e) {
return Msg.fail("PasswordError");
}
//设置登陆过期时间,单位毫秒,这里设置30分钟
SecurityUtils.getSubject().getSession().setTimeout(1800000);
return Msg.ok("Success");
}
}
2、PageController
@Controller
public class PageController {
@Resource
private UserMapper userMapper;
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String Login(){
return "login";
}
@RequestMapping(value = "/user/index", method = RequestMethod.GET)
public String Index(Model model){
User user = (User) SecurityUtils.getSubject().getPrincipal();
User uuser = userMapper.getUserById(user.getId());
if (StringUtils.isEmpty(user)) {
return "redirect:/login";
}
model.addAttribute("user", uuser);
return "index";
}
}
3、MenuController
/**
* @author wxhntmy
*/
@RestController
public class MenuController {
@Resource
private RoleMapper roleMapper;
@Resource
private UserRoleListMapper userRoleListMapper;
//记住用户菜单收展
private Map<String, Map> menu_map = new HashMap<>();
@RequestMapping(value = "/menu", method = RequestMethod.GET)
public Msg<List<Menu>> getMenu() {
User user = (User) SecurityUtils.getSubject().getPrincipal();
List<Menu> list = new ArrayList<>();
if (StringUtils.isEmpty(user)) {
return Msg.fail(list, "登录信息已过期!请重新登录!");
}
//记住收展
Map<String, Boolean> map_store = new HashMap<>();
if (menu_map.containsKey(user.getId())){
map_store = menu_map.get(user.getId());
}
Map以上是关于LayUI + Shiro 实现动态菜单并记住菜单收展的主要内容,如果未能解决你的问题,请参考以下文章