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');
        }
    }
})
上一篇:大厂面试解析


下一篇:uni-app微信小程序保持登录状态(vuex和本地存储)