1、编写一个新的认证对象继承自AuthenticationToken,当然也可以直接使用AuthenticationToken
package com.mochenli.eternalstar.shiro.token;
import com.mochenli.eternalstar.common.util.TokenUtil;
import org.apache.shiro.authc.AuthenticationToken;
public class JwtToken implements AuthenticationToken {
private String username;
private String token;
public JwtToken(String token) {
this.token = token;
this.username = TokenUtil.analysisTokenByUserName(token);
}
public JwtToken(){
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Object getPrincipal() {
return username;
}
@Override
public Object getCredentials() {
return token;
}
}
2、编写一个过滤器JwtFliter继承自AuthenticatingFilter
package com.mochenli.eternalstar.shiro.filter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.mochenli.eternalstar.common.enums.ExceptionCode;
import com.mochenli.eternalstar.common.exception.BaseException;
import com.mochenli.eternalstar.common.util.ApiResponse;
import com.mochenli.eternalstar.common.util.CookiesUtil;
import com.mochenli.eternalstar.common.util.TokenUtil;
import com.mochenli.eternalstar.shiro.token.JwtToken;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@EqualsAndHashCode(callSuper = true)
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JwtFilter extends AuthenticatingFilter {
private String token;
private boolean enabledTest;
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
httpServletResponse.setContentType("application/json");
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.addHeader("Access-Control-Max-Age", "3600");
httpServletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
httpServletResponse.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
return executeLogin(servletRequest, httpServletResponse);
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (httpServletRequest.getMethod().equalsIgnoreCase(OPTIONS_METHOD)) {
return true;
}
JwtToken token = this.createToken(request, response);
if (token == null) {
throw new BaseException(ExceptionCode.UNAUTHORIZED.getCode(), "没有凭证!");
}
Subject subject = this.getSubject(request, response);
subject.login(token);
return this.onLoginSuccess(token, subject, request, response);
} catch (Exception var5) {
return this.onLoginFailure( var5, request, response);
}
}
@Override
protected JwtToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String authorToken = httpServletRequest.getHeader("Authorization");
String tokenToCookies = CookiesUtil.tokenToCookies(httpServletRequest, "token");
String[] tokens = {authorToken, tokenToCookies};
String token = TokenUtil.tokenChoice(tokens, "mochenli");
if(enabledTest&&authorToken!=null&&authorToken.equals(this.token)) {
JwtToken jwtToken = new JwtToken();
jwtToken.setToken("swagger");
jwtToken.setUsername("swaggerTest");
return jwtToken;
}
if (StrUtil.isNotBlank(token)) {
return new JwtToken(token);
}
return null;
}
protected boolean onLoginFailure (Exception e, ServletRequest
request, ServletResponse response){
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
ApiResponse apiResponse =null;
Throwable throwable = e.getCause() == null ? e : e.getCause();
if(throwable instanceof BaseException){
BaseException baseException = (BaseException) throwable;
apiResponse = new ApiResponse(baseException.getStatusCode(), baseException.getMessage(), null);
}else{
apiResponse = new ApiResponse(ExceptionCode.UNAUTHORIZED.getCode(), throwable.getMessage(), null);
}
throwable.printStackTrace();
String jsonStr = JSONUtil.toJsonStr(apiResponse);
httpResponse.setStatus(apiResponse.getStatus());
httpResponse.getWriter().print(jsonStr);
} catch (IOException ignored) {
}
return false;
}
}
3、自定义Realm实现授权认证
package com.mochenli.eternalstar.shiro.config;
import cn.hutool.core.date.DateUtil;
import com.mochenli.eternalstar.common.enums.ExceptionCode;
import com.mochenli.eternalstar.common.exception.BaseException;
import com.mochenli.eternalstar.shiro.token.JwtToken;
import com.mochenli.eternalstar.system.entity.SystemToken;
import com.mochenli.eternalstar.system.mapper.SystemTokenMapper;
import com.mochenli.eternalstar.system.service.SystemMenuService;
import com.mochenli.eternalstar.system.service.SystemRoleService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@Component
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private SystemMenuService systemMenuService;
@Autowired
private SystemRoleService systemRoleService;
@Autowired
private SystemTokenMapper systemTokenMapper;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
String userName = (String)principalCollection.getPrimaryPrincipal();
Set<String> stringSet = new HashSet<>();
if("swaggerTest".equals(userName)){
stringSet = systemMenuService.selectMenuAll();
info.setStringPermissions(stringSet);
return info;
}
boolean rootBoolean = systemRoleService.selectByUserName(userName);
if(rootBoolean){
stringSet = systemMenuService.selectMenuAll();
}else{
stringSet = systemMenuService.selectMenuByUserId(userName);
}
info.setStringPermissions(stringSet);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
JwtToken token = (JwtToken) authenticationToken;
if("swagger".equals(token.getToken())&&"swaggerTest".equals(token.getUsername())){
return new SimpleAuthenticationInfo(token.getUsername(), token.getCredentials(), getName());
}
SystemToken systemToken = systemTokenMapper.selectOneById(token.getUsername());
if(systemToken == null||systemToken.getExpirationTime().before(new Date())){
throw new BaseException(ExceptionCode.UNAUTHORIZED.getCode(),"凭证过期!");
}
CompletableFuture.runAsync(() -> {
systemToken.setExpirationTime(DateUtil.offsetMinute(new Date(), 30));
systemTokenMapper.insertOrUpdate(systemToken);
});
return new SimpleAuthenticationInfo(token.getUsername(), token.getCredentials(), getName());
}
}
4、定义一个ShiroConfig配置类将安全管理器和过滤器工厂注册成bean,在安全管理器当中设置自定义的Realm,以及将前面定义的过滤器添加自需要授权认证的请求路径
package com.mochenli.eternalstar.shiro.config;
import com.mochenli.eternalstar.knife4j.properties.SpringdocTestProperties;
import com.mochenli.eternalstar.shiro.filter.JwtFilter;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Autowired
ShiroRealm shiroRealm;
@Autowired
SpringdocTestProperties springdocTestProperties;
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
Map<String, Filter> filerMap = new LinkedHashMap<>();
filerMap.put("jwtFiler",new JwtFilter(springdocTestProperties.getToken(),springdocTestProperties.isEnabledTest()));
shiroFilter.setFilters(filerMap);
shiroFilter.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/doc.html", "anon");
filterChainDefinitionMap.put("/v3/api-docs/**", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/**", "jwtFiler");
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilter;
}
}