apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统

点击上方Java开发联盟,选择“星标公众号”

优质文章,第一时间送达

简介:

前后端分离:

         要实现前后端分离,需要考虑以下2个问题:1. 项目不再基于session了,如何知道访问者是谁?2. 如何确认访问者的权限?

        前后端分离,一般都是通过token实现,本项目也是一样;用户登录时,生成token及 token过期时间,token与用户是一一对应关系,调用接口的时候,把token放到header或 请求参数中,服务端就知道是谁在调用接口。

        这次我们使用Shiro快速搭建前后端分离的权限管理系统 利用JPA帮我们管理数据库,Swagger Knife4j 帮我搭建Web测试环境;

后台基于:Springboot +JPA +Knife4j +Shiro

前端基于:VUE +ElementUI


代码已上传到Git:

后台代码:https://github.com/FENGZHIJIE1998/shiro-auth 

前端代码:https://github.com/FENGZHIJIE1998/shiro-vue

注意:主要观察token的使用方法!


1、新建SpringBoot工程

项目目录结构:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第1张图片

2、pom文件下用到的依赖

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  4.0.0      org.springframework.boot    spring-boot-starter-parent    2.3.2.RELEASE        com.example.demo2  demo2  0.0.1-SNAPSHOT  demo2  Demo project for Spring Boot      1.8                                org.springframework.boot            spring-boot-starter                                    org.springframework.boot            spring-boot-starter-test            test                                    org.springframework.boot            spring-boot-starter-web                                    org.springframework.boot            spring-boot-starter-validation                                    org.springframework.boot            spring-boot-starter-data-jpa                                    org.springframework.boot            spring-boot-starter-jdbc                                    org.projectlombok            lombok                                    org.apache.shiro            shiro-spring            1.3.2                                    mysql            mysql-connector-java            runtime                                    com.alibaba            druid-spring-boot-starter            1.1.10                                    com.spring4all            swagger-spring-boot-starter            1.8.0.RELEASE                                    com.github.xiaoymin            knife4j-spring-boot-starter            2.0.2                                    commons-lang            commons-lang            2.6                              org.springframework.boot        spring-boot-maven-plugin            

3、配置文件——数据库相关配置

1、application.yml文件

server:  port: 8080  tomcat:    uri-encoding: utf-8spring:  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/myweb?serverTimezone=UTC&useSSL=false    username: root    password: 123456  resources:    static-locations: classpath:/templates

这里的数据库地址一定要填写正确,还有就是你连接数据库的用户名和密码!

4、编写登陆入口

为了方便这里不做密码加盐加密:

@RestControllerpublic class ShiroController {    private final ShiroService shiroService;    public ShiroController(ShiroService shiroService) {        this.shiroService = shiroService;    }        /**     * 登录     */    @ApiOperation(value = "登陆", notes = "参数:用户名 密码")    @PostMapping("/sys/login")    public Map login(@RequestBody @Validated LoginDTO loginDTO, BindingResult bindingResult) {        Map result = new HashMap<>();        if (bindingResult.hasErrors()) {            result.put("status", 400);            result.put("msg", bindingResult.getFieldError().getDefaultMessage());            return result;        }        String username = loginDTO.getUsername();        String password = loginDTO.getPassword();        //用户信息        User user = shiroService.findByUsername(username);        //账号不存在、密码错误        if (user == null || !user.getPassword().equals(password)) {            result.put("status", 400);            result.put("msg", "账号或密码有误");        } else {            //生成token,并保存到数据库            result = shiroService.createToken(user.getUserId());            result.put("status", 200);            result.put("msg", "登陆成功");        }        return result;    }    /**     * 退出     */    @ApiOperation(value = "登出", notes = "参数:token")    @PostMapping("/sys/logout")    public Map logout(@RequestHeader("token")String token) {        Map result = new HashMap<>();        shiroService.logout(token);        result.put("status", 200);        result.put("msg", "您已安全退出系统");        return result;    }}

5、编写ShiroService中的方法

主要是生成一个token返回给前端。

@Servicepublic class ShiroServiceImpl implements ShiroService {     @Autowired    private UserRepository userRepository;    @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 createToken(Integer userId) {        Map 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);        }        result.put("token", token);        result.put("expire", EXPIRE);        return result;    }    @Override    public void logout(String token) {        SysToken byToken = findByToken(token);        //生成一个token        token = TokenGenerator.generateValue();        //修改token        SysToken tokenEntity = new SysToken();        tokenEntity.setUserId(byToken.getUserId());        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);    }}

6、编写ShiroConfig类

@Configurationpublic class ShiroConfig {    @Bean("securityManager")    public SecurityManager securityManager(AuthRealm authRealm) {        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();        securityManager.setRealm(authRealm);        securityManager.setRememberMeManager(null);        return securityManager;    }    @Bean("shiroFilter")    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();        shiroFilter.setSecurityManager(securityManager);        //oauth过滤        Map filters = new HashMap<>();        filters.put("auth", new AuthFilter());        shiroFilter.setFilters(filters);        Map 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;    }}

7、实现自定义的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类 */public class AuthToken extends UsernamePasswordToken{    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中却可以,可能是版本的问题)

        随后修改:为了避免误导,将上诉代码 AuthenticationToken 修改为 UsernamePasswordToken,并且走了一下源码,发现这个getAuthenticationTokenClass()实际上获取到的是UsernamePasswordToken.class

2f7e9390c747eb23022339ecba882993.png

69be87ac39d76e2c3df274c8959ae117.png

再回头看看renren-fast中的源码,原来他重写了supports方法!

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第2张图片

8、前端代码

        发起请求时,接受传过来的token后,如何保证token有效及用户权限呢?调用接口时,接受传过来的token后,如何保证token有效及用户权限呢?其实,Shiro提供了AuthorizingRealm以及AuthenticatingFilter抽象类,继承AuthorizingRealm和AuthenticatingFilter抽象类重写方法即可。

@Componentpublic class AuthRealm extends AuthorizingRealm {    @Autowired    private ShiroService shiroService;    @Override    /**     * 授权 获取用户的角色和权限     *@param  [principals]     *@return org.apache.shiro.authz.AuthorizationInfo     */    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    /**     * 认证 判断token的有效性     *@param  [token]     *@return org.apache.shiro.authc.AuthenticationInfo     */    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        //获取token,既前端传入的token        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;    }}

9、实现自定义AuthenticatingFilter。

/** * Shiro自定义auth过滤器 */@Componentpublic class AuthFilter extends AuthenticatingFilter {    // 定义jackson对象    private static final ObjectMapper MAPPER = new ObjectMapper();    /**     * 生成自定义token     *     * @param request     * @param response     * @return     * @throws Exception     */    @Override    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {        //获取请求token        String token = TokenUtil.getRequestToken((HttpServletRequest) request);        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 = TokenUtil.getRequestToken((HttpServletRequest) request);        if (StringUtils.isBlank(token)) {            HttpServletResponse httpResponse = (HttpServletResponse) response;            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());            httpResponse.setCharacterEncoding("UTF-8");            Map result = new HashMap<>();            result.put("status", 400);            result.put("msg", "请先登录");            String json = MAPPER.writeValueAsString(result);            httpResponse.getWriter().print(json);            return false;        }        return executeLogin(request, response);    }    /**     * token失效时候调用     */    @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", HttpContextUtil.getOrigin());        httpResponse.setCharacterEncoding("UTF-8");        try {            //处理登录失败的异常            Throwable throwable = e.getCause() == null ? e : e.getCause();            Map result = new HashMap<>();            result.put("status", 400);            result.put("msg", "登录凭证已失效,请重新登录");            String json = MAPPER.writeValueAsString(result);            httpResponse.getWriter().print(json);        } catch (IOException e1) {        }        return false;    }}

10、详解校验流程

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第3张图片


接着我们打上断点按照代码走走。

1、前端发起请求首先会进入AuthFilter的 isAccessAllowed(),除了OPTION方法,其余都拦截。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第4张图片

2、拦截之后进入AuthFilter的onAccessDenied(),这里获取token后判断token是否isBlank。如果是,代表请求未携带token,直接默认返回400,未登录给前端,流程就结束了。如果携带了token则进入第三步,继续流程。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第5张图片

3. 接着进入AuthFilter的createToken,这里生成我们自定义的AuthToken对象。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第6张图片

4. 接着就会来到AuthRealm中的doGetAuthenticationInfo(),在这个方法中继续token的有效性校验,例如过期、和数据库的token对不上(用户已退出)的情况。如果校验失败,进入第5步,否则进入第6步。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第7张图片

5. token失效后回到AuthFilter中的onLoginFailure(),返回400以及msg,流程结束。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第8张图片

6. Token校验成功后进入AuthRealm的doGetAuthorizationInfo(),进行获取当前用户拥有的权限,之后底层代码会进行权限验证。如果用户有权限则会进入请求方法,否则抛出异常。到这一步校验过程就结束了。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第9张图片

熬完上面的步骤了,这时候总体的架构已经确立好了,下面让我们来看看效果如何

DTO

/** * 登录传输类 */@Datapublic class LoginDTO {    @NotBlank(message = "用户名不能为空")    private String username;    @NotBlank(message = "密码不能为空")    private String password;}

实体类

@Getter@Setter@Entitypublic 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 roles;}@Getter@Setter@Entitypublic 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 permissions;}@Getter@Setter@Entitypublic class Permission {    @Id    private Integer permissionId;    private String permissionName;    private String permission;}@Getter@Setter@Entitypublic 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 TransferSource Server         : localhostSource Server Version : 50549Source Host           : localhost:3306Source Database       : shiroTarget Server Type    : MYSQLTarget Server Version : 50549File Encoding         : 65001*/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;-- ------------------------------ Table structure for sys_token-- ----------------------------CREATE TABLE `sys_token` (  `user_id` int(11) NOT NULL,  `expire_time` datetime DEFAULT NULL,  `token` varchar(255) DEFAULT NULL,  `update_time` datetime DEFAULT NULL,  PRIMARY KEY (`user_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。

@RestController("/test")public class TestController {    @RequiresPermissions({"save"}) //没有的话 AuthorizationException    @PostMapping("/save")    public Map save(String token) {        System.out.println("save");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有save的权力");        return map;    }    @RequiresPermissions({"delete"}) //没有的话 AuthorizationException    @DeleteMapping("/delete")    public Map delete(String token) {        System.out.println("delete");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有delete的权力");        return map;    }    @RequiresPermissions({"update"}) //没有的话 AuthorizationException    @PutMapping("update")    public Map update(String token) {        System.out.println("update");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有update的权力");        return map;    }    @RequiresPermissions({"select"}) //没有的话 AuthorizationException    @GetMapping("select")    public Map select(String token, HttpSession session) {        System.out.println("select");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有select的权力");        return map;    }    @RequiresRoles({"vip"}) //没有的话 AuthorizationException    @GetMapping("/vip")    public Map vip(String token) {        System.out.println("vip");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有VIP角色");        return map;    }    @RequiresRoles({"svip"}) //没有的话 AuthorizationException    @GetMapping("/svip")    public Map svip(String token) {        System.out.println("svip");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有SVIP角色");        return map;    }    @RequiresRoles({"p"}) //没有的话 AuthorizationException    @GetMapping("/p")    public Map p(String token) {        System.out.println("p");        Map map = new HashMap();        map.put("status", 200);        map.put("msg", "当前用户有P角色");        return map;    }}

ExceptionHandler 异常处理器,用于捕获无权限时候的异常。

@ControllerAdvicepublic class MyExceptionHandler {    @ExceptionHandler(value = AuthorizationException.class)    @ResponseBody    public Map handleException(AuthorizationException e) {        //e.printStackTrace();        Map result = new HashMap();        result.put("status", "400");        //获取错误中中括号的内容        String message = e.getMessage();        String msg=message.substring(message.indexOf("[")+1,message.indexOf("]"));        //判断是角色错误还是权限错误        if (message.contains("role")) {            result.put("msg", "对不起,您没有" + msg + "角色");        } else if (message.contains("permission")) {            result.put("msg", "对不起,您没有" + msg + "权限");        } else {            result.put("msg", "对不起,您的权限有误");        }        return result;    }}

启动项目来看看效果: 访问 localhost:98080/shiro/doc.html

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第10张图片

登陆成功:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第11张图片

登录成功后会返回token,记得带上token访问以下接口

有某个角色时候:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第12张图片

没有某个角色的时候:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第13张图片

有某个权力时候:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第14张图片

没有某个权力的时候:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第15张图片

退出系统:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第16张图片

原本的token就失效了,我们再访问原本可以访问的接口看看

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第17张图片

VUE+Element前端页面

正常访问:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第18张图片

非法访问:

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第19张图片

重点:当未登录时候访问项目内部页面,由前端控制路由返回登录页,并不会出现可恶的login.jsp,这里我们故意改变数据库token来展示效果。

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第20张图片

总结

        至于最后没有权利或角色返回的json字符串是因为他抛出AuthorizationException。可以自定义全局异常处理器进行处理。通过这种token达到即可达到前后端分离开发。各位客官,点个赞吧

简单小例子,仅供学习参考!

温暖提示

为了方便大家更好的学习,本公众号经常分享项目干货源码案例给大家去练习,如果本公众号没有你要学习的功能案例,你可以联系小编(微信:wcy18898375730)提供你帮助哦!

推荐案例

1、springboot+Mybatis+vue前后端分离开发:作业管理系统

2、SpringMVC +Spring+ Mybatis 的完整小项目

3、springboot+mybatis实现excel文件上传

4、springboot+mybatis+vue实现注册登录功能

5、Java多线程总结(一)

6、多线程之间实现同步(二)

apache shiro 如何升级_Springboot +Shiro +VUE 前后端分离之权限管理系统_第21张图片

你可能感兴趣的:(apache,shiro,如何升级,scope,参数错误或没有,scope,权限,shiro,前后端分离,shiro权限管理,springboot,jwt,token前后端分离)