• shiro在前后分离的权限管理


    shiro在前后分离的权限管理

    后端

    依赖

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.crazycake</groupId>
        <artifactId>shiro-redis</artifactId>
        <version>3.2.3</version>
    </dependency>
    

    redis配置

    spring:
      redis:
        host: localhost
        port: 6379
    

    UserRealm根据角色授权和登录认证

    public class UserRealm extends AuthorizingRealm {
        @Autowired
        private UserService userService;
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            UserResult userResult = (UserResult) principals.getPrimaryPrincipal();
            Set<String> role = Collections.singleton(userResult.getRole());
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.setRoles(role);
            return info;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken UPtoken = (UsernamePasswordToken) token;
            String username = UPtoken.getUsername();
            String password = new String(UPtoken.getPassword());
            User user = userService.queryUserByUsername(username);
            if (user != null && user.getPassword().equals(password)) {
                UserResult userResult = new UserResult(user);
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userResult, password, this.getName());
                return info;
            }
            return null;
        }
    }
    

    SessionManager获取前端传递的头信息的Authorization数据

    public class SessionManager extends DefaultWebSessionManager {
        @Override
        protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
    
            String id = WebUtils.toHttp(request).getHeader("Authorization");
            if (StringUtils.isEmpty(id)) {
                return super.getSessionId(request, response);
            } else {
                id = id.replaceAll("Bearer ", "");
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
                return id;
            }
        }
    }
    

    ShiroConfig配置部分页面和开启注解以及redis相关

    @Configuration
    public class ShiroConfig {
        @Bean
        public UserRealm userRealm() {
            return new UserRealm();
        }
    
        @Bean
        public DefaultWebSecurityManager securityManager(UserRealm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(userRealm);
            securityManager.setSessionManager(webSessionManager());
            securityManager.setCacheManager(redisCacheManager());
            return securityManager;
        }
    
        @Bean
        public ShiroFilterFactoryBean bean(DefaultWebSecurityManager securityManager) {
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            bean.setSecurityManager(securityManager);
            //未登录和未授权url
            bean.setLoginUrl("/authError?code=1");
            bean.setUnauthorizedUrl("/authError?code=2");
            Map<String, String> filterMap = new LinkedHashMap<>();
            //无需认证页面
            filterMap.put("/authError", "anon");
            filterMap.put("/login", "anon");
            filterMap.put("/info", "anon");
            filterMap.put("/register", "anon");
            filterMap.put("/guest/**", "anon");
            //swagger页面
            filterMap.put("/swagger-ui.html", "anon");
            filterMap.put("/swagger-resources/**", "anon");
            filterMap.put("/v2/api-docs/**", "anon");
            filterMap.put("/webjars/springfox-swagger-ui/**", "anon");
            filterMap.put("/configuration/**", "anon");
    	//其他页面
            filterMap.put("/**", "authc");
            bean.setFilterChainDefinitionMap(filterMap);
    
            return bean;
        }
    	
        //从配置文件读取redis连接
        @Value("${spring.redis.host}")
        private String host;
        @Value("${spring.redis.port}")
        private int port;
    
        public RedisManager redisManager() {
            return new RedisManager();
        }
    
        public RedisSessionDAO redisSessionDAO() {
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
            redisSessionDAO.setRedisManager(redisManager());
            return redisSessionDAO;
        }
    
        public DefaultWebSessionManager webSessionManager() {
            SessionManager sessionManager = new SessionManager();
            sessionManager.setSessionDAO(redisSessionDAO());
            //禁用cookie
            sessionManager.setSessionIdCookieEnabled(false);
            //禁用url重写
            sessionManager.setSessionIdUrlRewritingEnabled(false);
            return sessionManager;
        }
    
        public RedisCacheManager redisCacheManager() {
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(redisManager());
            return redisCacheManager;
        }
    
    
        //开启注解
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        @Bean
        @DependsOn(value = "lifecycleBeanPostProcessor")
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
            return creator;
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    }
    

    controller

    未登录和未授权返回相应的信息给前端

    @RequiresGuest
    @GetMapping("/authError")
    public Result authError(int code) {
        return code == 1 ? new Result(ResultCode.UNAUTHENTICATED) : new Result(ResultCode.UNAUTHORISE);
    }
    

    controller上通过@RequiresRoles验证用户的角色控制访问

    @RequiresRoles(value={"root","管理员"},logical = Logical.OR)
    @ApiOperation("用户分页")
    @GetMapping("/users/{page}/{size}")
    public Result queryUserList(@PathVariable("page") int page, @PathVariable("size") int size) {
        PageHelper.startPage(page, size);
        return new Result(ResultCode.SUCCESS, new PageInfo<>(userService.queryUserList()));
    }
    

    无权限用户访问抛出异常

    @ControllerAdvice
    public class ExceptionController {
        @ExceptionHandler(UnauthorizedException.class)
        @ResponseBody
        public Result UnauthorizedException(){
            return new Result(ResultCode.UNAUTHORISE);
        }
    }
    

    前端

    安装vuex

    npm install vuex --save
    npm install js-cookie --save
    

    store/index.js保存token和用户信息

    import Vue from "vue";
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
        state: {
            token: '',
            userInfo: {
                username: '',
                realname: '',
                status: '',
                role: ''
            }
        },
        mutations: {
            set_token(state, token){
                state.token = token;
            },
            set_userInfo(state, userInfo) {
                state.userInfo.username = userInfo.username;
                state.userInfo.realname = userInfo.realname;
                state.userInfo.status = userInfo.status;
                state.userInfo.role = userInfo.role;
            },
            remove_token(state){
                state.token = '';
            },
            remove_userInfo(state){
                state.userInfo.username = '';
                state.userInfo.name = '';
                state.userInfo.status = '';
                state.userInfo.role = ''
            }
        }
    })
    

    main.js导入store

    import store from './store'
    
    new Vue({
      el: '#app',
      router,
      store,
      components: {App},
      template: '<App/>',
      render: h => h(App),
    })
    

    utils/cookies.js方法工具类

    import Cookies from 'js-cookie'
    
    export function getCookies(key) {
        return Cookies.get(key)
    }
    
    export function setCookies(key, value) {
        Cookies.set(key, value)
    }
    
    export function removeCookies(key) {
        Cookies.remove(key)
    }
    

    vue下导入所需要的方法

    <script>
        import {getCookies, removeCookies} from "../utils/cookies";
    </script>
    

    login.vue

    提交带验证登录表单后, 接收后端返回的token存入store和cookie,在请求header中加入Authorization, 接收回来的用户信息存入store和cookie, 跳转到index页面

    onSubmit(formName) {
        const _this = this;
        this.$refs[formName].validate((valid) => {
            if (valid) {
                this.form.password=encrypt(this.form.plainpwd);
                this.$axios.post('http://localhost:8081/login', this.form).then(function (resp) {
                    if (resp.data.success) {
                        _this.$store.commit('set_token', resp.data.object);
                        setCookies('token', resp.data.object);
                        _this.$axios.post('http://localhost:8081/info', resp.data.object, {
                            headers: {
                                'Authorization': 'Bearer ' + resp.data.object
                            }
                        }).then(function (resp) {
                            // _this.userInfo = resp.data.object;
                            _this.$store.commit('set_userInfo', resp.data.object);
                            setCookies('userInfo', _this.$store.state.userInfo);
                            _this.$message({
                                type: 'success',
                                message: resp.data.message
                            });
                            _this.$router.push('/index')
                        });
                    } else {
                        _this.$message({
                            type: 'error',
                            message: resp.data.message
                        });
                    }
                })
            } else {
                return false;
            }
        });
    },
    

    前端通过axios访问后端的需认证页面时,均在header中定义Authorization放入token

    this.$axios.get('http://localhost:8081/solve/' + row.id, {
        headers: {
            'Authorization': 'Bearer ' + getCookies('token')
        }
    }
    

    router/permit.js

    权限验证

    • 已登录用户(cookie中存在token)
      • 访问游客页面---回到index页面
      • 普通用户访问用户页面, 白名单页面, 用户为root或管理员---放行
      • 其他---跳转至10003未授权页面
    • 未登录用户(cookie中不存在token)
      • 访问游客页面---放行
      • 其他---跳转至10003未授权页面
    import router from './index'
    
    import {getCookies} from "../utils/cookies"
    
    const guestRouter = ['/login', '/register', '/report'];
    const userRouter = ['/index', '/user/add', '/user/records', '/user/profile'];
    const whiteRouter = ['/', '/10003', '/404'];
    const admin = ['root', '管理员'];
    
    router.beforeEach(function (to, from, next) {
        if (getCookies('token')) {
            if (guestRouter.indexOf(to.path.toLowerCase()) !== -1) {
                next('index')
            } else if (userRouter.indexOf(to.path.toLowerCase()) !== -1 && JSON.parse(getCookies('userInfo')).role === '普通用户' || whiteRouter.indexOf(to.path.toLowerCase()) !== -1 || admin.indexOf(JSON.parse(getCookies('userInfo')).role) !== -1) {
                next()
            } else {
                next('/10003');
            }
        } else {
            if (guestRouter.concat(whiteRouter).indexOf(to.path.toLowerCase()) !== -1) {
                next();
            } else {
                next('/10003');
            }
        }
    })
    
  • 相关阅读:
    ffmpeg 简单使用总结
    使用 Solr 构建企业级搜索服务器
    Linux 常用命令整理
    基于.net standard 的动态编译实现
    基于.net core 微服务的另类实现
    Java中线程总结
    关于EF中直接执行sql语句的参数化问题
    一个关于单据审核的流程演变
    java中匿名内部类总结
    Eclipse 中打开选中文件/文件夹所在目录
  • 原文地址:https://www.cnblogs.com/pinked/p/12661472.html
Copyright © 2020-2023  润新知