授权拦截

This commit is contained in:
小蚂蚁 2022-03-28 23:01:38 +08:00
parent ef5d90621f
commit 5009482aac
10 changed files with 350 additions and 300 deletions

View File

@ -1,111 +0,0 @@
package com.hxkj.admin.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/**
* 安全管理器
*
* @param realmConfig realmConfig
* @return DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager doDefaultWebSecurityManager(ShiroRealm realmConfig) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realmConfig);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
defaultWebSecurityManager.setSubjectDAO(subjectDAO);
return defaultWebSecurityManager;
}
/**
* 拦截过滤器配置
* anon: 无需认证就可以访问
* authc: 必须认证了才能访问
* perms: 拥有某个资源权限才能访问
* role: 拥有某个角色权限才能访问
* @return ShiroFilterFactoryBean
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean doShiroFactoryFilterBean(DefaultWebSecurityManager defaultWebSecurityManager) {
// 设置安全管理器
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(defaultWebSecurityManager);
// 设置内置过滤器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap .put("/user/add", "authc");
filterChainDefinitionMap .put("/user/update", "authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap );
// 设置自定过滤器
LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("jwt", jwtFilter());
bean.setFilters(filterMap);
// 返回构建配置
return bean;
}
/**
* SpringShiroFilter首先注册到spring容器
* 然后被包装成FilterRegistrationBean
* 最后通过FilterRegistrationBean注册到servlet容器
*
* @return FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy(){
FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean = new FilterRegistrationBean<>();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setFilter(proxy);
return filterRegistrationBean;
}
/**
* 设置加密次数
*
* @return HashedCredentialsMatcher
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(1024);// 设置加密次数
return hashedCredentialsMatcher;
}
// 自定realm
@Bean
public ShiroRealm userRealm() {
return new ShiroRealm();
}
@Bean
public JwtFilter jwtFilter() {
return new JwtFilter();
}
}

View File

@ -1,89 +0,0 @@
package com.hxkj.admin.config;
import com.hxkj.admin.service.ISysAdminService;
import com.hxkj.common.entity.system.SysAdmin;
import org.apache.catalina.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.apache.shiro.subject.Subject;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
public class ShiroRealm extends AuthorizingRealm {
@Resource
ISysAdminService iSysAdminService;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
* 授权: 根据认证数据验证用户权限
*
* @param principals 包含了所有已认证的安全数据
* @return AuthorizationInfo 授权数据
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 1获取用户安全数据
Integer adminId = (Integer) principals.getPrimaryPrincipal();
// 2根据用户ID查询用户
// 3查询用户角色权限
List<String> perms = new ArrayList<>();
perms.add("user:add");
perms.add("user:update");
List<String> roles = new ArrayList<>();
roles.add("role1");
roles.add("role2");
// 4构造返回
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(perms);
info.addRoles(roles);
return info;
}
/**
* 认证: 校验用户名和密码是否一致
*
* @param auth 令牌
* @return AuthenticationInfo
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {
System.out.println("靠靠靠靠靠靠靠");
String token = (String) auth.getCredentials();
// 登录参数
// UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// String username = upToken.getUsername();
// String password = new String(upToken.getPassword());
//
// // 验证用户
// SysAdmin sysAdmin = iSysAdminService.findByUsername(username);
// if (sysAdmin == null) {
// return null;
// } else if (!password.equals(sysAdmin.getPassword())) {
// return null;
// }
// 登录成功
return new SimpleAuthenticationInfo(1, "13", "shiroRealm");
}
}

View File

@ -1,6 +1,5 @@
package com.hxkj.admin.config;
package com.hxkj.admin.config.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
@ -12,64 +11,6 @@ import javax.servlet.http.HttpServletResponse;
public class JwtFilter extends BasicHttpAuthenticationFilter {
/**
* 执行登录认证(判断请求头是否带上token)
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
System.out.println("酷酷酷酷酷");
// 如果请求头不存在token,则可能是执行登陆操作或是游客状态访问,直接返回true
if (isLoginAttempt(request, response)) {
return true;
}
// 如果存在,则进入executeLogin方法执行登入,检查token 是否正确
try {
executeLogin(request, response);return true;
} catch (Exception e) {
throw new AuthenticationException("Token失效请重新登录");
}
}
/**
* 判断用户是否是登入,检测headers里是否包含token字段
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
// if(antPathMatcher.match("/userLogin",req.getRequestURI())){
// return true;
// }
// String token = req.getHeader("token");
// if (token == null) {
// return false;
// }
// Object o = redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token);
// if(ObjectUtils.isEmpty(o)){
// return false;
// }
return true;
}
/**
* 重写AuthenticatingFilter的executeLogin方法执行登陆操作
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("token");//Access-Token
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获, 反之则代表登入成功,返回true
getSubject(request, response).login(jwtToken);return true;
}
/**
* 对跨域提供支持
*/
@ -87,4 +28,56 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
return super.preHandle(request, response);
}
/**
* 判断用户是否想要登入
* 检测header里面是否包含Authorization字段即可
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authorization");
return authorization != null;
}
/**
* 执行登录认证
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginAttempt(request, response)) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
this.responseUnAuth(response);
return false;
}
}
return true;
}
/**
* 将非法请求跳转到 401
*/
private void responseUnAuth(ServletResponse resp) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
httpServletResponse.sendRedirect("/user/unauthorized");
} catch (Exception ignored) {
}
}
/**
* 过滤器,每次登陆的时候,都从Header的Authorization,
* 获取到token,然后执行登陆的操作
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Authorization");
JwtToken jwtToken = new JwtToken(token);
getSubject(request, response).login(jwtToken);
return true;
}
}

View File

@ -1,4 +1,4 @@
package com.hxkj.admin.config;
package com.hxkj.admin.config.shiro;
import org.apache.shiro.authc.AuthenticationToken;
@ -8,18 +8,37 @@ import org.apache.shiro.authc.AuthenticationToken;
*/
public class JwtToken implements AuthenticationToken {
/**
* 令牌
*/
private final String token;
/**
* 构造方法
*/
public JwtToken(String token) {
System.out.println("就将计就计");
super();
this.token = token;
}
/**
* 获取Token
*/
public String getToken() {
return token;
}
/**
* 获取账号
*/
@Override
public Object getPrincipal() {
return token;
}
/**
* 获取密码
*/
@Override
public Object getCredentials() {
return token;

View File

@ -0,0 +1,123 @@
package com.hxkj.admin.config.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/**
* 过滤器注册
*/
@Bean
public FilterRegistrationBean<Filter> doFilterRegistrationBean() {
FilterRegistrationBean<Filter> filterRegistration = new FilterRegistrationBean<>();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistration.addInitParameter("targetFilterLifecycle", "true");
filterRegistration.setEnabled(true);
filterRegistration.addUrlPatterns("/");
return filterRegistration;
}
/**
* 生命周期对象
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor doLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 添加注解支持
*/
@Bean
public DefaultAdvisorAutoProxyCreator doDefaultAdvisorAutoProxyCreator() {
// // 强制使用cglib防止重复代理和可能引起代理出错的问题
DefaultAdvisorAutoProxyCreator dap = new DefaultAdvisorAutoProxyCreator();
dap.setProxyTargetClass(true);
return dap;
}
/**
* 自定realm
*/
@Bean
public ShiroRealm shiroRealm() {
return new ShiroRealm();
}
/**
* 安全管理器
*/
@Bean
public DefaultWebSecurityManager doDefaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(this.shiroRealm());
securityManager.setCacheManager(new MemoryConstrainedCacheManager());
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
evaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(evaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
/**
* 权限源切面
*/
@Bean
public AuthorizationAttributeSourceAdvisor doAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* 拦截过滤器配置
* anon: 无需认证就可以访问
* authc: 必须认证了才能访问
* perms: 拥有某个资源权限才能访问
* role: 拥有某个角色权限才能访问
* @return ShiroFilterFactoryBean
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean doShiroFactoryFilterBean(DefaultWebSecurityManager securityManager) {
// 设定认证管理
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
// 添加自定过滤器
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("jwt", new JwtFilter());
bean.setFilters(filterMap);
// 添加授权链接
Map<String,String> chainMap = new LinkedHashMap<>();
chainMap.put("/user/add", "authc");
// chainMap.put("/user/update", "jwt");
chainMap.put("/user/login", "anon");
chainMap.put("/**", "jwt");
bean.setFilterChainDefinitionMap(chainMap);
// 返回构建配置
return bean;
}
}

View File

@ -0,0 +1,73 @@
package com.hxkj.admin.config.shiro;
import com.hxkj.admin.service.ISysAdminService;
import com.hxkj.common.utils.JwtUtil;
import org.apache.shiro.authc.*;
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 javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ShiroRealm extends AuthorizingRealm {
@Resource
ISysAdminService iSysAdminService;
/**
* 重写判断token是否是JwtToke
*
* @param token AuthenticationToken
* @return boolean
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
* 授权: 根据认证数据验证用户权限
*
* @param principals 包含了所有已认证的安全数据
* @return AuthorizationInfo 授权数据
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("————权限认证————");
Set<String> perms = new HashSet<>();
Set<String> roles = new HashSet<>();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(perms);
info.addRoles(roles);
return info;
}
/**
* 认证: 校验用户名和密码是否一致
*
* @param authenticationToken token
* @return AuthenticationInfo
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
JwtToken jwtToken = (JwtToken) authenticationToken;
String token = jwtToken.getToken();
Integer userId = JwtUtil.getAudience(token);
if (userId == null || userId <= 0) {
throw new AuthenticationException("用户异常");
}
return new SimpleAuthenticationInfo(token, token, "shiroRealm");
}
}

View File

@ -1,13 +1,12 @@
package com.hxkj.admin.controller.system;
import com.hxkj.admin.config.shiro.JwtToken;
import com.hxkj.admin.service.ISysAdminService;
import com.hxkj.admin.validate.SysLoginParam;
import com.hxkj.common.core.AjaxResult;
import com.hxkj.common.entity.system.SysAdmin;
import com.hxkj.common.utils.JwtUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@ -15,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@ -22,6 +22,9 @@ import java.util.Map;
@RequestMapping("/api/system")
public class SysLoginController {
@Resource
ISysAdminService iSysAdminService;
/**
* 登录系统
*
@ -31,23 +34,24 @@ public class SysLoginController {
*/
@PostMapping("/login")
public Object login(@Validated() @RequestBody SysLoginParam sysLoginParam) {
String username = sysLoginParam.getUsername();
String password = sysLoginParam.getPassword();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
System.out.println("斤斤计较");
// 查询用户
SysAdmin sysAdmin = iSysAdminService.findByUsername(username);
if (sysAdmin == null || sysAdmin.getIsDelete() == 1) {
return AjaxResult.failed("账号或密码错误");
}
// 生成令牌
String tokenStr = JwtUtil.createToken(sysAdmin.getId());
JwtToken token = new JwtToken(tokenStr);
// 登录用户
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return AjaxResult.success();
} catch (AuthenticationException e) {
String msg = "用户或密码错误";
if (!e.getMessage().equals("")) {
msg = e.getMessage();
}
return AjaxResult.failed(msg);
}
return AjaxResult.success("登录成功", tokenStr);
}
}

View File

@ -33,8 +33,8 @@ spring:
mybatis-plus:
mapper-locations: classpath*:/mapper/**Mapper.xml # 映射文件路径
typeAliasesPackage: com.hxkj.**.mapper
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# configuration:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
db-config:

View File

@ -91,6 +91,18 @@ public class AjaxResult {
return new AjaxResult(code, msg, new ArrayList<>());
}
/**
* 成功返回结果
*
* @author fzr
* @param msg 提示信息
* @param data 响应数据
* @return AjaxResult
*/
public static AjaxResult success(String msg, Object data) {
return new AjaxResult(HttpEnum.SUCCESS.getCode(), msg, data);
}
/**
* 成功返回结果
*

View File

@ -3,7 +3,10 @@ package com.hxkj.common.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import java.util.Calendar;
import java.util.Date;
@ -33,36 +36,59 @@ public class JwtUtil {
* 创建token
*
* @author fzr
* @param map 参数
* @param userId 用户ID
*/
public static String createToken(Map<String, String> map) {
public static String createToken(Integer userId) {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND, expire);
JWTCreator.Builder builder = JWT.create();
map.forEach(builder::withClaim);
return builder.withExpiresAt(instance.getTime())
.withIssuedAt(new Date())
.sign(Algorithm.HMAC256(secret));
return JWT.create()
.withAudience(String.valueOf(userId)) // 签发对象
.withIssuedAt(new Date()) // 发行时间
.withExpiresAt(instance.getTime()) // 有效时间
.withClaim("userId", userId) // 载荷
.sign(Algorithm.HMAC256(secret)); // 加密
}
/**
* token是否过期
* @author fzr
* @param token token
* 检验合法性
*
* @param token 令牌
*/
public void isTokenExpired(String token) {
JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
public static void verifyToken(String token) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
verifier.verify(token);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
/**
* 获取token中的payload
* @author fzr
* @param token token
* @return DecodedJWT
* 获取签发对象
*
* @param token 令牌
* @return userId
*/
public static DecodedJWT getToken(String token) {
return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
public static Integer getAudience(String token) {
Integer audience = null;
try {
audience = Integer.parseInt(JWT.decode(token).getAudience().get(0));
} catch (JWTDecodeException j) {
System.out.println(j.getMessage());
}
return audience;
}
/**
* 通过载荷名字获取载荷的值
*
* @param token 令牌
* @param name 载荷名称
* @return Claim
*/
public static Claim getClaimByName(String token, String name){
return JWT.decode(token).getClaim(name);
}
}