Springboot +Shiro 前后端分離式權限管理系統


 前后端分離
要實現前后端分離,需要考慮以下2個問題: 1. 項目不再基於session了,如何知道訪問者是誰? 2. 如何確認訪問者的權限?


前后端分離,一般都是通過token實現,本項目也是一樣;用戶登錄時,生成token及 token過期時間,token與用戶是一一對應關系,調用接口的時候,把token放到header或 請求參數中,服務端就知道是誰在調用接口。

代碼已上傳到Git:https://github.com/FENGZHIJIE1998/shiro-demo

Let's do it!!


介紹:這次我們使用SpringBoot+SpringJPA+Swagger+Shiro快速搭建前后端分離的權限管理系統 利用JPA幫我們管理數據庫,Swagger幫我搭建Web測試環境;注意:主要觀察token的使用方法!

第一步:新建工程,pom文件application.yml巴拉巴拉這里省略,這里貼出需要用到的依賴

 
<dependencies>
 
<dependency>
 
<groupId>org.springframework.boot</groupId>
 
<artifactId>spring-boot-starter</artifactId>
 
</dependency>
 
 
 
<dependency>
 
<groupId>org.springframework.boot</groupId>
 
<artifactId>spring-boot-starter-test</artifactId>
 
<scope>test</scope>
 
</dependency>
 
 
 
<!--web-->
 
<dependency>
 
<groupId>org.springframework.boot</groupId>
 
<artifactId>spring-boot-starter-web</artifactId>
 
</dependency>
 
 
 
<!--JPA-->
 
<dependency>
 
<groupId>org.springframework.boot</groupId>
 
<artifactId>spring-boot-starter-data-jpa</artifactId>
 
</dependency>
 
 
 
<!--JDBC-->
 
<dependency>
 
<groupId>org.springframework.boot</groupId>
 
<artifactId>spring-boot-starter-jdbc</artifactId>
 
</dependency>
 
 
 
<!--lombok-->
 
<dependency>
 
<groupId>org.projectlombok</groupId>
 
<artifactId>lombok</artifactId>
 
</dependency>
 
 
 
<!-- shiro-->
 
<dependency>
 
<groupId>org.apache.shiro</groupId>
 
<artifactId>shiro-spring</artifactId>
 
<version>1.3.2</version>
 
</dependency>
 
<!--mysql-connector-->
 
<dependency>
 
<groupId>mysql</groupId>
 
<artifactId>mysql-connector-java</artifactId>
 
<scope>runtime</scope>
 
</dependency>
 
<!--druid-->
 
<dependency>
 
<groupId>com.alibaba</groupId>
 
<artifactId>druid</artifactId>
 
<version>1.0.31</version>
 
</dependency>
 
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
 
<dependency>
 
<groupId>com.alibaba</groupId>
 
<artifactId>druid-spring-boot-starter</artifactId>
 
<version>1.1.10</version>
 
</dependency>
 
<!-- swagger -->
 
<dependency>
 
<groupId>com.spring4all</groupId>
 
<artifactId>swagger-spring-boot-starter</artifactId>
 
<version>1.8.0.RELEASE</version>
 
</dependency>
 
<!-- swagger生成接口API -->
 
<dependency>
 
<groupId>io.springfox</groupId>
 
<artifactId>springfox-swagger2</artifactId>
 
<version>2.7.0</version>
 
</dependency>
 
<!-- 接口API生成html文檔 -->
 
<dependency>
 
<groupId>io.springfox</groupId>
 
<artifactId>springfox-swagger-ui</artifactId>
 
<version>2.7.0</version>
 
</dependency>
 
<!--json工具-->
 
<dependency>
 
<groupId>com.alibaba</groupId>
 
<artifactId>fastjson</artifactId>
 
<version>1.2.13</version>
 
</dependency>
 
<!--gson -->
 
<dependency>
 
<groupId>com.google.code.gson</groupId>
 
<artifactId>gson</artifactId>
 
<version>2.8.5</version>
 
</dependency>
 
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
 
<dependency>
 
<groupId>commons-lang</groupId>
 
<artifactId>commons-lang</artifactId>
 
<version>2.6</version>
 
</dependency>
 
 
 
</dependencies>

 

第二步:准備好要用的包包和類類

第三步:編寫登陸入口(為了方便這里不做密碼加鹽加密):

/**
 
* @Author CrazyJay
 
* @Date 2019/3/30 22:04
 
* @Version 1.0
 
*/
 
@RestController
 
public class ShiroController {
 
 
 
@Autowired
 
private ShiroService shiroService;
 
 
 
/**
 
* 登錄
 
*/
 
@ApiOperation(value = "登陸", notes = "參數:用戶名 密碼")
 
@GetMapping("/sys/login")
 
public Map<String, Object> login(String username, String password) {
 
Map<String, Object> result = new HashMap<>();
 
//用戶信息
 
User user = shiroService.findByUsername(username);
 
//賬號不存在、密碼錯誤
 
if (user==null||!user.getPassword().equals(password)) {
 
result.put("status", "400");
 
result.put("msg", "賬號或密碼有誤");
 
return result;
 
} else {
 
//生成token,並保存到數據庫
 
result = shiroService.createToken(user.getUserId());
 
result.put("status", "200");
 
result.put("msg", "登陸成功");
 
return result;
 
}
 
}
 
 
 
/**
 
* 退出
 
*/
 
@PostMapping("/sys/logout")
 
public Map<String, Object> logout() {
 
Map<String, Object> result = new HashMap<>();
 
User user = (User) SecurityUtils.getSubject().getPrincipal();
 
shiroService.logout(user.getUserId());
 
result.put("status", "200");
 
result.put("msg", "登陸成功");
 
return result;
 
}
 
}

 

第四步:編寫ShiroService中的方法

 
/**
 
* @Author CrazyJay
 
* @Date 2019/3/30 22:18
 
* @Version 1.0
 
*/
 
@Service
 
public class ShiroServiceImpl implements ShiroService {
 
 
 
 
 
@Autowired
 
private UserRepository userRepository;
 
@Autowired
 
private RoleRepository roleRepository;
 
@Autowired
 
private PermissionRepository permissionRepository;
 
@Autowired
 
private SysTokenRepository sysTokenRepository;
 
 
 
/**
 
* 根據username查找用戶
 
*
 
* @param username
 
* @return User
 
*/
 
@Override
 
public User findByUsername(String username) {
 
User user = userRepository.findByUsername(username);
 
return user;
 
}
 
 
 
//12小時后過期
 
private final static int EXPIRE = 3600 * 12;
 
 
 
@Override
 
/** 重點!!
 
* 生成一個token
 
*@param [userId]
 
*@return Result
 
*/
 
public Map<String,Object> createToken(Integer userId) {
 
Map<String,Object> result = new HashMap<>();
 
//生成一個token
 
String token = TokenGenerator.generateValue();
 
//當前時間
 
Date now = new Date();
 
//過期時間
 
Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
 
//判斷是否生成過token
 
SysToken tokenEntity = sysTokenRepository.findByUserId(userId);
 
if (tokenEntity == null) {
 
tokenEntity = new SysToken();
 
tokenEntity.setUserId(userId);
 
tokenEntity.setToken(token);
 
tokenEntity.setUpdateTime(now);
 
tokenEntity.setExpireTime(expireTime);
 
//保存token
 
sysTokenRepository.save(tokenEntity);
 
} else {
 
tokenEntity.setToken(token);
 
tokenEntity.setUpdateTime(now);
 
tokenEntity.setExpireTime(expireTime);
 
//更新token
 
sysTokenRepository.save(tokenEntity);
 
}
 
//返回token給前端
 
result.put("token", token);
 
result.put("expire", EXPIRE);
 
return result;
 
}
 
 
 
@Override
 
public void logout(Integer userId) {
 
//生成一個token
 
String token = TokenGenerator.generateValue();
 
//修改token
 
SysToken tokenEntity = new SysToken();
 
tokenEntity.setUserId(userId);
 
tokenEntity.setToken(token);
 
sysTokenRepository.save(tokenEntity);
 
}
 
 
 
@Override
 
public SysToken findByToken(String accessToken) {
 
return sysTokenRepository.findByToken(accessToken);
 
 
 
}
 
 
 
@Override
 
public User findByUserId(Integer userId) {
 
return userRepository.findByUserId(userId);
 
}
 
}

 

第四步:編寫ShiroConfig類

 
/**
 
* @Author CrazyJay
 
* @Date 2019/3/30 21:50
 
* @Version 1.0
 
*/
 
@Configuration
 
public class ShiroConfig {
 
 
 
@Bean("securityManager")
 
public SecurityManager securityManager(AuthRealm authRealm) {
 
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 
//設置自定義的Realm
 
securityManager.setRealm(authRealm);
 
securityManager.setRememberMeManager(null);
 
return securityManager;
 
}
 
 
 
@Bean("shiroFilter")
 
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
 
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
 
shiroFilter.setSecurityManager(securityManager);
 
//自定義過濾(關鍵)
 
Map<String, Filter> filters = new HashMap<>();
 
filters.put("auth", new AuthFilter());
 
shiroFilter.setFilters(filters);
 
Map<String, String> filterMap = new LinkedHashMap<>();
 
filterMap.put("/webjars/**", "anon");
 
filterMap.put("/druid/**", "anon");
 
filterMap.put("/sys/login", "anon");
 
filterMap.put("/swagger/**", "anon");
 
filterMap.put("/v2/api-docs", "anon");
 
filterMap.put("/swagger-ui.html", "anon");
 
filterMap.put("/swagger-resources/**", "anon");
 
filterMap.put("/**", "auth");
 
shiroFilter.setFilterChainDefinitionMap(filterMap);
 
//這里不需要設置什么登陸路徑之類的
 
return shiroFilter;
 
}
 
 
 
@Bean("lifecycleBeanPostProcessor")
 
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
 
return new LifecycleBeanPostProcessor();
 
}
 
 
 
@Bean
 
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
 
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
 
advisor.setSecurityManager(securityManager);
 
return advisor;
 
}
 
}

 

第五步:實現自定義AuthenticatingFilter。調用接口時,接受傳過來的token后,如何保證token有效及用戶權限呢?其實,Shiro提 供了AuthenticatingFilter抽象類,繼承AuthenticatingFilter抽象類即可。這個過濾器是實現前后端分離的重中之重!

/**
 
* auth過濾器
 
*
 
* @Author CrazyJay
 
* @Date 2019/3/31 10:38
 
* @Version 1.0
 
*/
 
public class AuthFilter extends AuthenticatingFilter {
 
 
 
/**
 
* 生成自定義token
 
* @param request
 
* @param response
 
* @return
 
* @throws Exception
 
*/
 
@Override
 
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
 
//獲取請求token
 
String token = getRequestToken((HttpServletRequest) request);
 
if (StringUtils.isBlank(token)) {
 
return null;
 
}
 
return new AuthToken(token);
 
}
 
 
 
/**
 
* 步驟1.所有請求全部拒絕訪問
 
* @param request
 
* @param response
 
* @param mappedValue
 
* @return
 
*/
 
@Override
 
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
 
if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
 
return true;
 
}
 
return false;
 
}
 
 
 
/**
 
* 步驟2,拒絕訪問的請求,會調用onAccessDenied方法,onAccessDenied方法先獲取 token,再調用executeLogin方法
 
* @param request
 
* @param response
 
* @return
 
* @throws Exception
 
*/
 
@Override
 
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
 
//獲取請求token,如果token不存在,直接返回
 
String token = getRequestToken((HttpServletRequest) request);
 
if (StringUtils.isBlank(token)) {
 
HttpServletResponse httpResponse = (HttpServletResponse) response;
 
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
 
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
 
httpResponse.setCharacterEncoding("UTF-8");
 
Map<String, Object> result = new HashMap<>();
 
result.put("status", "400");
 
result.put("msg", "未登錄--onAccessDenied");
 
String json = new Gson().toJson(result);
 
httpResponse.getWriter().print(json);
 
return false;
 
}
 
return executeLogin(request, response);
 
}
 
/**
 
* 登陸失敗時候調用
 
*/
 
@Override
 
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
 
HttpServletResponse httpResponse = (HttpServletResponse) response;
 
httpResponse.setContentType("application/json;charset=utf-8");
 
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
 
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
 
httpResponse.setCharacterEncoding("UTF-8");
 
try {
 
//處理登錄失敗的異常
 
Throwable throwable = e.getCause() == null ? e : e.getCause();
 
Map<String, Object> result = new HashMap<>();
 
result.put("status", "400");
 
result.put("msg", "登陸失敗--onLoginFailure");
 
String json = JSON.toJSONString(result);
 
httpResponse.getWriter().print(json);
 
} catch (IOException e1) {
 
}
 
return false;
 
}
 
/**
 
* 獲取請求的token
 
*/
 
private String getRequestToken(HttpServletRequest httpRequest) {
 
 
 
//從header中獲取token
 
String token = httpRequest.getHeader("token");
 
//如果header中不存在token,則從參數中獲取token
 
if (StringUtils.isBlank(token)) {
 
token = httpRequest.getParameter("token");
 
}
 
return token;
 
}
 
}

 

第六步:實現自定義的AuthenticationToken。閱讀AuthenticatingFilter抽象類中executeLogin方法,我們發現調用 了subject.login(token),這是shiro的登錄方法,且需要token參數,我們自定義 AuthToken類,只要實現AuthenticationToken接口,就可以了。

 
//AuthenticatingFilter中的executeLogin()
 
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
 
AuthenticationToken token = createToken(request, response);
 
if (token == null) {
 
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
 
"must be created in order to execute a login attempt.";
 
throw new IllegalStateException(msg);
 
}
 
try {
 
Subject subject = getSubject(request, response);
 
//重點!
 
subject.login(token);
 
return onLoginSuccess(token, subject, request, response);
 
} catch (AuthenticationException e) {
 
return onLoginFailure(token, e, request, response);
 
}
 
}
 
/**
 
* 自定義AuthenticationToken類
 
* @Author CrazyJay
 
* @Date 2019/3/31 10:58
 
* @Version 1.0
 
*/
 
public class AuthToken implements AuthenticationToken {
 
 
 
private String token;
 
 
 
public AuthToken(String token) {
 
this.token = token;
 
}
 
 
 
@Override
 
public Object getPrincipal() {
 
return token;
 
}
 
 
 
@Override
 
public Object getCredentials() {
 
return token;
 
}
 
}

 

這里我實現的時候出現了Token不匹配的Bug。DeBug下可以查到源頭是代碼是用UsernamePasswordToken.class和我自定義的AuthToken.class配對。按道理應該是true,卻返回了false...於是我就把自定義的AuthToken不實現AuthenticationToken,轉為繼承UsernamePasswordToken,就可以了。(renren-fast中卻可以,可能是版本的問題)

第七步:編寫自己的Realm

/**
 
* @Author CrazyJay
 
* @Date 2019/3/30 21:38
 
* @Version 1.0
 
*/
 
@Component
 
public class AuthRealm extends AuthorizingRealm {
 
 
 
@Autowired
 
private ShiroService shiroService;
 
 
 
/**
 
* 授權(驗證權限時候調用
 
*@param [principals]
 
*@return org.apache.shiro.authz.AuthorizationInfo
 
*/
 
@Override
 
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
 
//1. 從 PrincipalCollection 中來獲取登錄用戶的信息
 
User user = (User) principals.getPrimaryPrincipal();
 
//Integer userId = user.getUserId();
 
//2.添加角色和權限
 
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
 
for (Role role : user.getRoles()) {
 
//2.1添加角色
 
simpleAuthorizationInfo.addRole(role.getRoleName());
 
for (Permission permission : role.getPermissions()) {
 
//2.1.1添加權限
 
simpleAuthorizationInfo.addStringPermission(permission.getPermission());
 
}
 
}
 
return simpleAuthorizationInfo;
 
}
 
 
 
@Override
 
/**
 
* 認證(登陸時候調用)
 
*@param [token]
 
*@return org.apache.shiro.authc.AuthenticationInfo
 
*/
 
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
 
String accessToken = (String) token.getPrincipal();
 
//1. 根據accessToken,查詢用戶信息
 
SysToken tokenEntity = shiroService.findByToken(accessToken);
 
//2. token失效
 
if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
 
throw new IncorrectCredentialsException("token失效,請重新登錄");
 
}
 
//3. 調用數據庫的方法, 從數據庫中查詢 username 對應的用戶記錄
 
User user = shiroService.findByUserId(tokenEntity.getUserId());
 
//4. 若用戶不存在, 則可以拋出 UnknownAccountException 異常
 
if (user == null) {
 
throw new UnknownAccountException("用戶不存在!");
 
}
 
//5. 根據用戶的情況, 來構建 AuthenticationInfo 對象並返回. 通常使用的實現類為: SimpleAuthenticationInfo
 
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, this.getName());
 
return info;
 
}
 
}

 

第八步:,登錄失敗后,則調用AuthFilter中的onLoginFailure(),進行失敗處理,整個流程結束。

/**
 
* 登陸失敗時候調用
 
*/
 
@Override
 
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
 
HttpServletResponse httpResponse = (HttpServletResponse) response;
 
httpResponse.setContentType("application/json;charset=utf-8");
 
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
 
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
 
httpResponse.setCharacterEncoding("UTF-8");
 
try {
 
//處理登錄失敗的異常
 
Throwable throwable = e.getCause() == null ? e : e.getCause();
 
Map<String, Object> result = new HashMap<>();
 
result.put("status", "400");
 
result.put("msg", "登陸失敗--onLoginFailure");
 
String json = JSON.toJSONString(result);
 
httpResponse.getWriter().print(json);
 
} catch (IOException e1) {
 
}
 
return false;
 
}

 

第九步:登錄成功后,則調用doGetAuthorizationInfo方法,查詢用戶的權限,再調用具體的接口,整個流程結束。

/**
 
* 授權(驗證權限時候調用
 
*@param [principals]
 
*@return org.apache.shiro.authz.AuthorizationInfo
 
*/
 
@Override
 
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
 
//1. 從 PrincipalCollection 中來獲取登錄用戶的信息
 
User user = (User) principals.getPrimaryPrincipal();
 
//Integer userId = user.getUserId();
 
//2.添加角色和權限
 
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
 
for (Role role : user.getRoles()) {
 
//2.1添加角色
 
simpleAuthorizationInfo.addRole(role.getRoleName());
 
for (Permission permission : role.getPermissions()) {
 
//2.1.1添加權限
 
simpleAuthorizationInfo.addStringPermission(permission.getPermission());
 
}
 
}
 
return simpleAuthorizationInfo;
 
}

 

然后我們來看看效果:

實體類

@Getter
 
@Setter
 
@Entity
 
public class User {
 
@Id
 
private Integer userId;
 
 
 
private String username;
 
private String password;
 
 
 
@ManyToMany(fetch = FetchType.EAGER)
 
@JoinTable(name = "user_role",
 
joinColumns = {@JoinColumn(name = "USER_ID", referencedColumnName = "userId")},
 
inverseJoinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "roleId")})
 
private Set<Role> roles;
 
 
 
}
 
 
 
@Getter
 
@Setter
 
@Entity
 
public class Role {
 
 
 
@Id
 
private Integer roleId;
 
private String roleName;
 
 
 
@ManyToMany(fetch = FetchType.EAGER)
 
@JoinTable(name = "role_permission",
 
joinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "roleId")},
 
inverseJoinColumns = {@JoinColumn(name = "PERMISSION_ID", referencedColumnName = "permissionId")})
 
private Set<Permission> permissions;
 
}
 
 
 
@Getter
 
@Setter
 
@Entity
 
public class Permission {
 
 
 
@Id
 
private Integer permissionId;
 
private String permissionName;
 
private String permission;
 
}
 
 
 
@Getter
 
@Setter
 
@Entity
 
public class SysToken{
 
 
 
@Id
 
private Integer userId;
 
private String token;
 
private Date expireTime;
 
private Date updateTime
 
}

 

以及給實體類附上權限:

我定義了三個用戶 

用戶 角色 權限
Jack SVIP select;save;delete;update
Rose VIP select;save;update
Paul P select
/*
 
Navicat MySQL Data Transfer
 
Source Server : localhost
 
Source Server Version : 50549
 
Source Host : localhost:3306
 
Source Database : shiro
 
Target Server Type : MYSQL
 
Target Server Version : 50549
 
File Encoding : 65001
 
Date: 2019-04-07 17:06:36
 
*/
 
 
 
SET FOREIGN_KEY_CHECKS=0;
 
 
 
-- ----------------------------
 
-- Table structure for permission
 
-- ----------------------------
 
DROP TABLE IF EXISTS `permission`;
 
CREATE TABLE `permission` (
 
`permission_id` int(11) NOT NULL,
 
`permission` varchar(255) DEFAULT NULL,
 
`permission_name` varchar(255) DEFAULT NULL,
 
PRIMARY KEY (`permission_id`)
 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
-- ----------------------------
 
-- Records of permission
 
-- ----------------------------
 
INSERT INTO `permission` VALUES ('1', 'select', '查看');
 
INSERT INTO `permission` VALUES ('2', 'update', '更新');
 
INSERT INTO `permission` VALUES ('3', 'delete', '刪除');
 
INSERT INTO `permission` VALUES ('4', 'save', '新增');
 
 
 
-- ----------------------------
 
-- Table structure for role
 
-- ----------------------------
 
DROP TABLE IF EXISTS `role`;
 
CREATE TABLE `role` (
 
`role_id` int(11) NOT NULL,
 
`role_name` varchar(255) DEFAULT NULL,
 
PRIMARY KEY (`role_id`)
 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
-- ----------------------------
 
-- Records of role
 
-- ----------------------------
 
INSERT INTO `role` VALUES ('1', 'svip');
 
INSERT INTO `role` VALUES ('2', 'vip');
 
INSERT INTO `role` VALUES ('3', 'p');
 
 
 
-- ----------------------------
 
-- Table structure for role_permission
 
-- ----------------------------
 
DROP TABLE IF EXISTS `role_permission`;
 
CREATE TABLE `role_permission` (
 
`role_id` int(11) NOT NULL,
 
`permission_id` int(11) NOT NULL,
 
PRIMARY KEY (`role_id`,`permission_id`),
 
KEY `FKf8yllw1ecvwqy3ehyxawqa1qp` (`permission_id`),
 
CONSTRAINT `FKa6jx8n8xkesmjmv6jqug6bg68` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`),
 
CONSTRAINT `FKf8yllw1ecvwqy3ehyxawqa1qp` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`permission_id`)
 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
-- ----------------------------
 
-- Records of role_permission
 
-- ----------------------------
 
INSERT INTO `role_permission` VALUES ('1', '1');
 
INSERT INTO `role_permission` VALUES ('2', '1');
 
INSERT INTO `role_permission` VALUES ('3', '1');
 
INSERT INTO `role_permission` VALUES ('1', '2');
 
INSERT INTO `role_permission` VALUES ('2', '2');
 
INSERT INTO `role_permission` VALUES ('1', '3');
 
INSERT INTO `role_permission` VALUES ('1', '4');
 
INSERT INTO `role_permission` VALUES ('2', '4');
 
 
 
-- ----------------------------
 
-- Table structure for user
 
-- ----------------------------
 
DROP TABLE IF EXISTS `user`;
 
CREATE TABLE `user` (
 
`user_id` int(11) NOT NULL,
 
`password` varchar(255) DEFAULT NULL,
 
`username` varchar(255) DEFAULT NULL,
 
PRIMARY KEY (`user_id`)
 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
-- ----------------------------
 
-- Records of user
 
-- ----------------------------
 
INSERT INTO `user` VALUES ('1', '123', 'Jack');
 
INSERT INTO `user` VALUES ('2', '123', 'Rose');
 
INSERT INTO `user` VALUES ('3', '123', 'Paul');
 
 
 
-- ----------------------------
 
-- Table structure for user_role
 
-- ----------------------------
 
DROP TABLE IF EXISTS `user_role`;
 
CREATE TABLE `user_role` (
 
`user_id` int(11) NOT NULL,
 
`role_id` int(11) NOT NULL,
 
PRIMARY KEY (`user_id`,`role_id`),
 
KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`),
 
CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`),
 
CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`)
 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
-- ----------------------------
 
-- Records of user_role
 
-- ----------------------------
 
INSERT INTO `user_role` VALUES ('1', '1');
 
INSERT INTO `user_role` VALUES ('2', '2');
 
INSERT INTO `user_role` VALUES ('3', '3');

 

 

測試類:因為我是用Swagger來測試,所以為了方便就直接傳遞token參數。具體開發時候可由前端把接收到的token放入Header。

/**
 
* @Author CrazyJay
 
* @Date 2019/4/7 15:20
 
* @Version 1.0
 
*/
 
@RestController("/test")
 
public class TestController {
 
 
 
 
 
@RequiresPermissions({"save"}) //沒有的話 AuthorizationException
 
@PostMapping("/save")
 
public Map<String, Object> selectRole(String token) {
 
 
 
System.out.println("save");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有save的權力");
 
return map;
 
}
 
 
 
@RequiresPermissions({"delete"}) //沒有的話 AuthorizationException
 
@PostMapping("/delete")
 
public Map<String, Object> managerRole(String token) {
 
System.out.println("delete");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有delete的權力");
 
return map;
 
}
 
 
 
@RequiresPermissions({"update"}) //沒有的話 AuthorizationException
 
@PostMapping("update")
 
public Map<String, Object> selectUser(String token) {
 
System.out.println("update");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有update的權力");
 
return map;
 
}
 
 
 
@RequiresPermissions({"select"}) //沒有的話 AuthorizationException
 
@PostMapping("select")
 
public Map<String, Object> managerUser(String token) {
 
System.out.println("select");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有select的權力");
 
return map;
 
}
 
 
 
@RequiresRoles({"vip"}) //沒有的話 AuthorizationException
 
@PostMapping("/vip")
 
public Map<String, Object> vip(String token) {
 
System.out.println("vip");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有VIP角色");
 
return map;
 
}
 
@RequiresRoles({"svip"}) //沒有的話 AuthorizationException
 
@PostMapping("/svip")
 
public Map<String, Object> SVIP(String token) {
 
System.out.println("svip");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有SVIP角色");
 
return map;
 
}
 
@RequiresRoles({"p"}) //沒有的話 AuthorizationException
 
@PostMapping("/p")
 
public Map<String, Object> P(String token) {
 
System.out.println("p");
 
Map<String, Object> map = new HashMap<String, Object>();
 
map.put("success", true);
 
map.put("msg", "當前用戶有P角色");
 
return map;
 
}
 
}

 

啟動項目來看看效果:記得帶上token

登陸成功:

 

登陸失敗:

 

有某個角色時候:

 

沒有某個角色的時候:

 

有某個權力時候:

 

沒有某個權力的時候:

 

至此就已經進入尾聲了

總結:

至於最后沒有權利或角色返回的json字符串可以因為他拋出AuthorizationException。可以自定義全局異常處理器進行處理。通過這種token達到即可達到前后端分離開發。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM