前后端分離
要實現前后端分離,需要考慮以下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達到即可達到前后端分離開發。