首先是表的設計,五張表:用戶,角色,菜單,用戶角色,菜單角色
菜單表的設計
其餘的表就沒什麼太多好說的了,都差不多,一個用戶可以對應多個角色,角色對應多個菜單
關於用戶登錄後 獲取對應的權限菜單樹 這類代碼就不貼出來了比較簡單 就一個查詢,查出對應的數據 完事丟給前端,前端再渲染成對應的樣式即可
重點貼上shiro整合springboot達到一個權限控制的代碼:
maven
<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.0</version>
</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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<exclusions>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
</exclusion>
</exclusions>
<version>1.4.0</version>
</dependency>
<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-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.28</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
spring的幫助類
package com.hw.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextRegister implements ApplicationContextAware {
private static Logger logger = LoggerFactory.getLogger(ApplicationContextRegister.class);
private static ApplicationContext APPLICATION_CONTEXT;
/**
* 設置spring上下文
* @param applicationContext spring上下文
* @throws BeansException
* */
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
logger.debug("ApplicationContext registed-->{}", applicationContext);
APPLICATION_CONTEXT = applicationContext;
}
/**
* 獲取容器
* @return
*/
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
/**
* 獲取容器對象
* @param type
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> type) {
return APPLICATION_CONTEXT.getBean(type);
}
}
session監聽
package com.hw.config;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import java.util.concurrent.atomic.AtomicInteger;
public class BDSessionListener implements SessionListener {
private final AtomicInteger sessionCount = new AtomicInteger(0);
@Override
public void onStart(Session session) {
sessionCount.incrementAndGet();
}
@Override
public void onStop(Session session) {
sessionCount.decrementAndGet();
}
@Override
public void onExpiration(Session session) {
sessionCount.decrementAndGet();
}
public int getSessionCount() {
return sessionCount.get();
}
}
shiro的配置類
package com.hw.shiro;
import com.hw.config.ApplicationContextRegister;
import com.hw.dao.UserDao;
import com.hw.doman.UserDO;
import com.hw.service.MenuService;
import com.hw.util.MD5Utils;
import com.hw.util.ShiroUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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 org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class UserRealm extends AuthorizingRealm {
/* @Autowired
UserDao userMapper;
@Autowired
MenuService menuService;*/
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 獲取菜單表中的權限標識
* @param arg0
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
Long userId = ShiroUtils.getUserId();
MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
Set<String> perms = menuService.listPerms(userId);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(perms);
return info;
}
/**
* 登錄
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
Map<String, Object> map = new HashMap<>(16);
map.put("username", username);
String password = new String((char[]) token.getCredentials());
UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class);
List<UserDO> list = userMapper.list(map);
// 查詢用戶信息
UserDO user = list.get(0);
// 賬號不存在
if (user == null) {
throw new UnknownAccountException("賬號或密碼不正確");
}
// 密碼錯誤
if (!password.equals(user.getPassword())) {
throw new IncorrectCredentialsException("賬號或密碼不正確");
}
// 賬號鎖定
if (user.getStatus() == 0) {
throw new LockedAccountException("賬號已被鎖定,請聯繫管理員");
}
//將用戶的角色加入到用戶屬性中
Map<String, Object> map1 = jdbcTemplate.queryForMap("SELECT GROUP_CONCAT(role_id SEPARATOR ',') as roleid FROM `sys_user_role` where user_id=" + user.getUserId());
String[] roleids = map1.get("roleid").toString().split(",");
List<String> strings = Arrays.asList(roleids);
user.setRoleIds(new ArrayList(strings));
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
}
測試用的controller
package com.hw.controller;
import com.hw.util.MD5Utils;
import com.hw.util.R;
import com.hw.util.ShiroUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class TestController {
@GetMapping("/find")
@ResponseBody
public String find(){
return ShiroUtils.getUser().toString();
}
@GetMapping("/anniversary")
@RequiresPermissions("system:anniversary:anniversary")
String Birthday(){
return "成功";
}
@GetMapping("/anniversary2")
@RequiresPermissions("system:anniversary:anniversary2")
String Birthday2(){
return "成功";
}
@PostMapping("/login")
R ajaxLogin(String username, String password, HttpServletRequest request) {
password = MD5Utils.encrypt(username, password);
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
System.err.println(ShiroUtils.getUser().toString());
return R.ok();
} catch (AuthenticationException e) {
return R.error("用戶或密碼錯誤");
}
}
@GetMapping("/test")
public String test() {
return "test";
}
}
示例:
登錄成功後 請求自己有權限的接口
請求自己沒有權限的接口 報錯 (當然,這只是這只是用於測試案例,所以對錯誤不做處理,正式項目需要對這類異常進行配置處理)