这篇文章主要介绍如何使用Spring Boot+Shiro实现权限管理,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
一:配置pom.xml文件
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency>
二:ShiroConfig配置类
@Configurationpublic class ShiroConfig {@Bean("sessionManager")public SessionManager sessionManager() {DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setGlobalSessionTimeout(24 * 60 * 60 * 1000);// 开启会话验证器sessionManager.setSessionValidationSchedulerEnabled(true);// 删除失效的sessionsessionManager.setDeleteInvalidSessions(true);// 指定sessionId,使用默认的“JSESSIONID”sessionManager.setSessionIdCookieEnabled(true);return sessionManager;}@Bean("securityManager")public SecurityManager securityManager(OAuth3Realm oAuth3Realm, SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(oAuth3Realm);securityManager.setSessionManager(sessionManager);return securityManager;}@Bean("shiroFilter")public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);// oauth过滤Map<String, Filter> filters = new HashMap<>();filters.put("oauth3", new OAuth3Filter());shiroFilter.setFilters(filters);// 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行Map<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/webjars//@Bean//public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {//DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();//proxyCreator.setProxyTargetClass(true);//return proxyCreator;//}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}
其中,如果启用了DefaultAdvisorAutoProxyCreator的话,会导致二次代理的问题,Realm中的doGetAuthorizationInfo会重复调用2次。
三:自定义Shiro Filter类OAuth3Filter
public class OAuth3Filter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { //获取请求token String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isNullOrEmpty(token)){ return null; } return new OAuth3Token(token); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { //现在vue项目中使用axios发送http请求,每次请求都会多一次Request Method: OPTIONS请求,称为“预检”请求 if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){ return true; } return false; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //获取请求token,如果token不存在,直接返回401 String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isNullOrEmpty(token)){ HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin()); String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token")); 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()); try { //处理登录失败的异常 Throwable throwable = e.getCause() == null ? e : e.getCause(); R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage()); String json = new Gson().toJson(r); httpResponse.getWriter().print(json); } catch (IOException e1) { } return false; } private String getRequestToken(HttpServletRequest httpRequest){ //从header中获取token String token = httpRequest.getHeader("token"); //如果header中不存在token,则从参数中获取token if(StringUtils.isNullOrEmpty(token)){ token = httpRequest.getParameter("token"); } return "112233445566";//token; }}
对于复杂的跨域请求,Vue会首先发送一个OPTIONS请求,进行验证。需要后端对所有接口统一处理放行OPTIONS方法(即返回200)即可
四:自定义Token
public class OAuth3Token implements AuthenticationToken {private String token;public OAuth3Token(String token) {this.token = token;}@Overridepublic String getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;}}
五:自定义OAuth3Realm
@Componentpublic class OAuth3Realm extends AuthorizingRealm {@Autowiredprivate ShiroService shiroService;@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof OAuth3Token;}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("=============获取已登录用户,权限信息============");SysUserEntity user = (SysUserEntity) principals.getPrimaryPrincipal();Long userId = user.getUserId();// 用户权限列表Set<String> permsSet = shiroService.getUserPermissions(userId);SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permsSet);return info;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("=============获取已登录用户,用户信息============");String accessToken = (String) token.getPrincipal();// 根据accessToken,查询用户信息SysUserTokenEntity tokenEntity = shiroService.queryByToken(accessToken);// token失效if (tokenEntity == null) {throw new IncorrectCredentialsException("token失效,请重新登录");}// 查询用户信息SysUserEntity user = shiroService.queryUser(tokenEntity.getUserId());// 账号锁定if (user.getStatus().equals("00")) {throw new LockedAccountException("账号已被锁定,请联系管理员");}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName() // 返回一个唯一的Realm名字);return info;}}
shiroService用来从数据库或者缓存中,查询用户信息和权限信息。
六:TokenGenerator
public class TokenGenerator { public static String generateValue() { return generateValue(UUID.randomUUID().toString()); } private static final char[] hexCode = "0123456789abcdef".toCharArray(); public static String toHexString(byte[] data) { if(data == null) { return null; } StringBuilder r = new StringBuilder(data.length*2); for ( byte b : data) { r.append(hexCode[(b >> 4) & 0xF]); r.append(hexCode[(b & 0xF)]); } return r.toString(); } public static String generateValue(String param) { try { MessageDigest algorithm = MessageDigest.getInstance("MD5"); algorithm.reset(); algorithm.update(param.getBytes()); byte[] messageDigest = algorithm.digest(); return toHexString(messageDigest); } catch (Exception e) { e.printStackTrace(); } return ""; }}
七:Controller层Demo
@RestController@RequestMapping("shiro")public class ShiroController {@RequestMapping(value = "/login", method = RequestMethod.GET)public String login() {return "login";}@RequestMapping(value = "/info", method = RequestMethod.GET)@RequiresPermissions("sys:config:info")public String info() {return "info";}}
八:登录代码
@PostMapping("/sys/login")public Map<String, Object> login(@RequestBody SysLoginForm form)throws IOException {boolean captcha = captchaService.validate(form.getUuid(), form.getCaptcha());if(!captcha){return R.error("验证码不正确");}//用户信息SysUserEntity user = sysUserService.queryByUserName(form.getUsername());//账号不存在、密码错误if(user == null || !user.getPassword().equals(new Sha256Hash(form.getPassword(), user.getSalt()).toHex())) {return R.error("账号或密码不正确");}//账号锁定if(Constant.CommonStatus.BANNED.getValue().equals(user.getStatus())){return R.error("账号已被锁定,请联系管理员");}//生成token,并保存到数据库R r = sysUserTokenService.createToken(user.getUserId());return r;}
登录验证,独立实现,验证成功后创建Token,Token放到Redis缓存中。前端页面请求接口时要带Token.
九:代码调用流程
1.登录成功后,创建Token,后面的接口调用都要传递Token
2.接口首先通过shiroFilter过滤,确定是否要进入OAuth3Filter进行处理
3.OAuth3Filter 首先执行isAccessAllowed方法,如果时OPTIONS请求直接放行,否则进行Shiro的executeLogin验证
4.executeLogin执行的时候会到OAuth3Realm中调用doGetAuthenticationInfo方法,根据Token获取当前登录用户的信息(Token与用户的关联,已在第八点自己实现的登录代码中实现)
5.executeLogin成功后,会看当前访问的接口,有无权限注解@RequiresPermissions
6.如果有注解的话,说需要进行权限验证,Shiro会通过OAuth3Realm的doGetAuthorizationInfo方法,获取当前用户的权限进行验证
以上是“如何使用Spring Boot+Shiro实现权限管理”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注编程网行业资讯频道!