api postmain 鉴权_单一入口的API接口使用shiro如果进行权限鉴权

自定义过滤器,重写

isAccessAllowed

自定义realm,重写

doGetAuthorizationInfo

我需要根据链接参数不同进行授权,就是通过这种方法的。

public class CustomRealm extends AuthorizingRealm

下面是授权代码

/**

* 获取权限

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

logger.info("开始鉴权------------doGetAuthorizationInfo");

User user = (User) principalCollection.getPrimaryPrincipal();

List userRoles = new ArrayList();

Set categoryIds = new HashSet<>();

QueryWrapper queryUserWrapper = new QueryWrapper();

queryUserWrapper.eq("uuid",user.getUuid());

user = userService.getOne(queryUserWrapper);

if(null != user){

Ugroup ugroup = ugroupService.getById(user.getGroupId());

userRoles.add(ugroup.getName());

QueryWrapper queryWrapper = new QueryWrapper();

queryWrapper.eq("idx_group_id",ugroup.getId());

List groupCategories = groupCategoryService.list(queryWrapper);

if (groupCategories.size() > 0){

for (GroupCategory groupCategory : groupCategories){

categoryIds.add(groupCategory.getCategoryId().toString());

}

}

}else{

throw new AuthorizationException();

}

//为当前用户设置角色和权限

SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

authorizationInfo.addRoles(userRoles);

authorizationInfo.addStringPermissions(categoryIds);

return authorizationInfo;

}

过滤器中重写isAccessAllowed

public class JWTFilter extends AuthorizationFilter {

...

@Override

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

logger.info("-----isAccessAllowed----");

if (isLoginAttempt(request, response)) {

try {

executeLogin(request, response);

} catch (Exception e) {

response401(response);

}

}

String id = request.getParameter("id");

Subject subject = getSubject(request,response);

boolean flag = subject.isPermitted(id);

return flag;

}

...

}

需求:对属于不同用户组的用户授权访问不同类型的文章

实现思路:

doGetAuthorizationInfo中根据token获取到用户信息,从数据库中联合查询到可以访问的文章类型id,添加到用户权限中,isAccessAllowed中获取用户访问链接中的参数,调用

subject.isPermitted(id);

判断是否拥有该权限。

注意点:subject.isPermitted(id)调用前一定要先调用subject.login(token);方法,不然在isPermitted时不会进行授权

下面贴上全部代码

CustomRealm.java

package com.zyc.shiro;

import com.baomidou.mybatisplus.core.conditions.Wrapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.zyc.constant.CommonConstant;

import com.zyc.dao.GroupCategoryMapper;

import com.zyc.dao.UgroupMapper;

import com.zyc.entity.GroupCategory;

import com.zyc.entity.Ugroup;

import com.zyc.entity.User;

import com.zyc.redis.JwtRedisDAO;

import com.zyc.service.GroupCategoryService;

import com.zyc.service.UgroupService;

import com.zyc.service.UserService;

import com.zyc.util.JWTUtils;

import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authc.SimpleAuthenticationInfo;

import org.apache.shiro.authc.UnknownAccountException;

import org.apache.shiro.authz.AuthorizationException;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

/**

* 鉴权

*/

public class CustomRealm extends AuthorizingRealm {

static Logger logger = LoggerFactory.getLogger(CustomRealm.class);

@Autowired

private UgroupService ugroupService;

@Autowired

private GroupCategoryService groupCategoryService;

@Autowired

private UserService userService;

@Autowired

private JwtRedisDAO jwtRedisDAO;

/**

* 必须重写此方法,不然Shiro会报错

*/

@Override

public boolean supports(AuthenticationToken token) {

return token instanceof JWTToken;

}

/**

* 获取权限

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

logger.info("开始鉴权------------doGetAuthorizationInfo");

User user = (User) principalCollection.getPrimaryPrincipal();

List userRoles = new ArrayList();

Set categoryIds = new HashSet<>();

QueryWrapper queryUserWrapper = new QueryWrapper();

queryUserWrapper.eq("uuid",user.getUuid());

user = userService.getOne(queryUserWrapper);

if(null != user){

Ugroup ugroup = ugroupService.getById(user.getGroupId());

userRoles.add(ugroup.getName());

QueryWrapper queryWrapper = new QueryWrapper();

queryWrapper.eq("idx_group_id",ugroup.getId());

List groupCategories = groupCategoryService.list(queryWrapper);

if (groupCategories.size() > 0){

for (GroupCategory groupCategory : groupCategories){

categoryIds.add(groupCategory.getCategoryId().toString());

}

}

}else{

throw new AuthorizationException();

}

//为当前用户设置角色和权限

SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

authorizationInfo.addRoles(userRoles);

authorizationInfo.addStringPermissions(categoryIds);

return authorizationInfo;

}

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {

logger.info("CustomRealm------------------doGetAuthenticationInfo");

Map claims = JWTUtils.getClaims(CommonConstant.JWT_SECRET, (String) authenticationToken.getPrincipal());

if (claims == null) {

//没找到帐号

throw new UnknownAccountException();

}

String uuid = (String) claims.get("uuid");

QueryWrapper queryWrapper = new QueryWrapper();

queryWrapper.eq("uuid",uuid);

User user = userService.getOne(queryWrapper);

//logger.info(user.toString());

if(null == user){

throw new UnknownAccountException();

}

String token = jwtRedisDAO.get(CommonConstant.ADMIN_JWT_PREFIX + uuid);

//logger.info("token {}", authenticationToken.getPrincipal());

if (token == null || !token.equals(authenticationToken.getPrincipal())) {

throw new AuthorizationException();

}

//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,可以自定义实现

return new SimpleAuthenticationInfo(

//用户信息

user,

authenticationToken.getPrincipal(),

//realm name

getName()

);

}

}

JWTFilter.java

package com.zyc.shiro;

import com.alibaba.fastjson.JSON;

import com.zyc.exception.enums.ErrorEnums;

import com.zyc.vo.RestResult;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;

import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.http.HttpStatus;

import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.io.PrintWriter;

public class JWTFilter extends AuthorizationFilter {

private static Logger logger = LoggerFactory.getLogger(JWTFilter.class);

/**

* 判断用户是否想要登入。

* 检测header里面是否包含Authorization字段即可

*/

//@Override

protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {

logger.info("-----isLoginAttempt----");

HttpServletRequest req = (HttpServletRequest) request;

String authorization = req.getHeader("Authorization");

return authorization != null;

}

/**

* 实现用户登录

*/

//@Override

protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {

logger.info("-----executeLogin----");

HttpServletRequest httpServletRequest = (HttpServletRequest) request;

String authorization = httpServletRequest.getHeader("Authorization");

JWTToken token = new JWTToken(authorization);

// 提交给realm进行登入,如果错误他会抛出异常并被捕获

getSubject(request, response).login(token);

// 如果没有抛出异常则代表登入成功,返回true

return true;

}

/**

* 该注释并非现在方法的注释,是以前版本的,不要受影响,写在这里只是提醒还有其他写法

*

* 这里我们详细说明下为什么最终返回的都是true,即允许访问

* 例如我们提供一个地址 GET /article

* 登入用户和游客看到的内容是不同的

* 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西

* 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入

* 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可

* 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大

*/

@Override

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

logger.info("-----isAccessAllowed----");

if (isLoginAttempt(request, response)) {

try {

executeLogin(request, response);

} catch (Exception e) {

response401(response);

}

}

String id = request.getParameter("id");

Subject subject = getSubject(request,response);

boolean flag = subject.isPermitted(id);

return flag;

}

/**

* 对跨域提供支持

*/

@Override

protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

logger.info("-----preHandle----");

HttpServletRequest httpServletRequest = (HttpServletRequest) request;

HttpServletResponse httpServletResponse = (HttpServletResponse) response;

httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));

httpServletResponse.setHeader("Access-Control-Allow-Methods", httpServletRequest.getMethod());

httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");

httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));

// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态

if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {

httpServletResponse.setStatus(HttpStatus.OK.value());

return true;

}

return super.preHandle(request, response);

}

/**

* onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回 true 表示需要继续处理;如果返回 false 表示该拦截器实例已经处理了,将直接返回即可。

* @param servletRequest

* @param servletResponse

* @return

* @throws Exception

*/

@Override

protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) {

logger.info("当 isAccessAllowed 返回 false 的时候,才会执行 method onAccessDenied ");

try {

HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

RestResult result = new RestResult();

result.setCode(ErrorEnums.PERMISSION_DENIED.getCode());

result.setMsg(ErrorEnums.PERMISSION_DENIED.getMsg());

httpServletResponse.setContentType("application/json;charset=UTF-8");

PrintWriter out = httpServletResponse.getWriter();

out.print(JSON.toJSONString(result));

out.flush();

out.close();

} catch (IOException e) {

logger.error(e.getMessage());

}

// 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)

return false;

}

/**

* 非法请求返回code401

*/

private void response401(ServletResponse resp) {

logger.info("-----response401----");

try {

HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

RestResult result = new RestResult();

result.setCode(ErrorEnums.TOKEN_MISS.getCode());

result.setMsg(ErrorEnums.TOKEN_MISS.getMsg());

httpServletResponse.setContentType("application/json;charset=UTF-8");

PrintWriter out = httpServletResponse.getWriter();

out.print(JSON.toJSONString(result));

out.flush();

out.close();

} catch (IOException e) {

logger.error(e.getMessage());

}

}

}

JWTToken.java

package com.zyc.shiro;

import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken {

private String token;

public JWTToken(String token) {

this.token = token;

}

@Override

public Object getPrincipal() {

return token;

}

@Override

public Object getCredentials() {

return getPrincipal();

}

}

ShiroConfigurer.java

package com.zyc;

import com.zyc.shiro.*;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

import org.apache.shiro.authz.ModularRealmAuthorizer;

import org.apache.shiro.authz.permission.PermissionResolver;

import org.apache.shiro.authz.permission.RolePermissionResolver;

import org.apache.shiro.mgt.DefaultSecurityManager;

import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;

import org.apache.shiro.mgt.DefaultSubjectDAO;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.realm.Realm;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;

import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.DependsOn;

import java.util.*;

@Configuration

public class ShiroConfigurer {

private static final Logger logger = LoggerFactory.getLogger(ShiroConfigurer.class);

/**

* Shiro的Web过滤器Factory 命名:shiroFilter

*

* @param securityManager

* @return

*/

@Bean(name = "shiroFilter")

public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {

logger.info("注入Shiro的Web过滤器-->shiroFilter {}", ShiroFilterFactoryBean.class);

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// Shiro的核心安全接口,这个属性是必须的

shiroFilterFactoryBean.setSecurityManager(securityManager);

// 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面

// shiroFilterFactoryBean.setLoginUrl("/login")

// 登录成功后要跳转的连接,逻辑也可以自定义,例如返回上次请求的页面

// shiroFilterFactoryBean.setSuccessUrl("/index")

// 用户访问未对其授权的资源时,所显示的连接

// shiroFilterFactoryBean.setUnauthorizedUrl("/pages/403")

/* 定义shiro过滤器,例如实现自定义的FormAuthenticationFilter,需要继承FormAuthenticationFilter

**本例中暂不自定义实现,在下一节实现验证码的例子中体现

*/

/*定义shiro过滤链 Map结构

* Map中key(xml中是指value值)的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的

* anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种

* authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter

*/

// 添加自己的过滤器并且取名为jwt

Map filterMap = new HashMap();

filterMap.put("jwt", new JWTFilter());

shiroFilterFactoryBean.setFilters(filterMap);

Map filterChainDefinitionMap = new LinkedHashMap<>();

// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了

// :这是一个坑呢,一不小心代码就不好使了

//

// filterChainDefinitionMap.put("/webui/**", "anon")

// filterChainDefinitionMap.put("/webjars/**", "anon")

//filterChainDefinitionMap.put("/sys/login", "anon");

//filterChainDefinitionMap.put("/sys/logout", "anon");

//filterChainDefinitionMap.put("/token/callback", "anon");

//filterChainDefinitionMap.put("/dist", "anon");

//filterChainDefinitionMap.put("/download/excel", "anon");

//登陆相关api不需要被过滤器拦截

filterChainDefinitionMap.put("/user/login", "anon");

filterChainDefinitionMap.put("/**", "authc");

// 所有请求通过JWT Filter

filterChainDefinitionMap.put("/**", "jwt");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

return shiroFilterFactoryBean;

}

@Bean

public JWTFilter jwtFilter() {

return new JWTFilter();

}

/**

* Shiro Realm 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的

*

* @param

* @return managerRealm

*/

@Bean

public CustomRealm userRealm() {

CustomRealm userRealm = new CustomRealm();

// 告诉realm,使用credentialsMatcher加密算法类来验证密文

// userRealm.setCredentialsMatcher(hashedCredentialsMatcher())

userRealm.setCachingEnabled(false);

//自定义权限解析器

return userRealm;

}

/**

* 不指定名字的话,自动创建一个方法名第一个字母小写的bean

*

* @return

* @Bean(name = "securityManager")

*/

@Bean

public SecurityManager securityManager() {

logger.info("注入Shiro的Web过滤器-->securityManager {}", ShiroFilterFactoryBean.class);

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

securityManager.setRealm(userRealm());

DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();

// 关闭自带session

DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();

defaultSessionStorageEvaluator.setSessionStorageEnabled(false);

subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);

securityManager.setSubjectDAO(subjectDAO);

return securityManager;

}

/**

* 凭证匹配器

* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了

* 所以我们需要修改下doGetAuthenticationInfo中的代码;

* )

* 可以扩展凭证匹配器,实现 输入密码错误次数后锁定等功能,下一次

*

* @return

*/

@Bean(name = "credentialsMatcher")

public HashedCredentialsMatcher hashedCredentialsMatcher() {

HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

hashedCredentialsMatcher.setHashAlgorithmName("SHA-512");

//散列的次数,比如散列两次,相当于 md5(md5(""))

hashedCredentialsMatcher.setHashIterations(2);

//storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码

hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);

return hashedCredentialsMatcher;

}

/**

* Shiro生命周期处理器

*

* @return

*/

@Bean

public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {

return new LifecycleBeanPostProcessor();

}

/**

* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证

* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能

*

* @return

*/

@Bean

@DependsOn({"lifecycleBeanPostProcessor"})

public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {

DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

advisorAutoProxyCreator.setProxyTargetClass(true);

advisorAutoProxyCreator.setUsePrefix(true);

return advisorAutoProxyCreator;

}

@Bean

public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {

AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());

return authorizationAttributeSourceAdvisor;

}

}

pom.xml

org.springframework.boot

spring-boot-starter-aop

io.jsonwebtoken

jjwt

0.7.0

org.apache.shiro

shiro-spring

1.3.2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值