前后端分离
要实现前后端分离,需要考虑以下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达到即可达到前后端分离开发。