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');
}
}
})