1.pom.xml导入shiro相关jar包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
2.封装Shiro配置类
在配置类中Realm,DefaultWebSecurityManager,ShiroFilterFactoryBean这三个Bean必须要配置的。
@Configuration
public class ShiroConfiguration {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("authc", new JwtFilter());
shiroFilterFactoryBean.setFilters(filters);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 登录接口排除
filterChainDefinitionMap.put("/login", "anon");
// 退出登录接口排除
filterChainDefinitionMap.put("/logout", "anon");
// 图形验证码接口排除
filterChainDefinitionMap.put("/captcha", "anon");
anon(filterChainDefinitionMap);
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
private void anon(Map<String, String> filterChainDefinitionMap) {
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/*.html", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/beta/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/layui/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/v2/api-docs/**", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/pages/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/statics/**", "anon");
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 多realm
Set<Realm> realms = new HashSet<>(MediaConstants.DEFAULT_COLLECTION_SIZE);
realms.add(adminRealm());
securityManager.setRealms(realms);
// 关闭session
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
securityManager.setAuthenticator(authenticator());
return securityManager;
}
@Bean
public Authenticator authenticator() {
ModularRealm modularRealm = new ModularRealm();
modularRealm.setRealms(Arrays.asList(adminRealm()));
modularRealm.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealm;
}
@Bean
public AdminShiroRealm adminRealm() {
AdminShiroRealm mobileRealm = new AdminShiroRealm();
mobileRealm.setName(LoginAdminTypeEnums.ADMIN_USER.value);
return mobileRealm;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
3.配置Realm
Realm中主要涉及用户身份验证和授权策略信息的设置
@Data
public class AdminShiroRealm extends AuthorizingRealm {
@Autowired
private UserAuthService userAuthMgr;
/**
* 使用JWT替代原生Token
*
* @param token
* @return
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
private static final String ADMIN_USER_LOGIN_TYPE = LoginAdminTypeEnums.ADMIN_USER.value;
{
// 设置realm的名字,非常重要
super.setName(ADMIN_USER_LOGIN_TYPE);
}
/**
* 功能: 获取用户权限信息,包括角色以及权限。只有当触发检测用户权限时才会调用此方法,例如checkRole,checkPermission
*
* @return AuthorizationInfo 权限信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String sid = JwtUtil.getSid(principals.toString());
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 查询用户权限
List<String> userAuthList = userAuthMgr.getAuthority(sid);
if (!CollectionUtils.isEmpty(userAuthList)) {
Set<String> userAuthorities = new HashSet<>(userAuthList);
authorizationInfo.setStringPermissions(userAuthorities);
}
return authorizationInfo;
}
/**
* 功能: 用来进行身份认证,也就是说验证用户输入的账号和密码是否正确,获取身份验证信息,错误抛出异常
*
* @return 返回封装了用户信息的 AuthenticationInfo 实例
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
if (StringUtils.isEmpty(token)) {
throw new AuthenticationException("token非法无效!");
}
String loginAccount = JwtUtil.getLoginAccount(token);
String loginType = JwtUtil.getLoginType(token);
if (loginAccount == null || loginType == null) {
throw new AuthenticationException("token非法无效!");
}
if (!JwtUtil.verify(token, loginAccount)) {
throw new AuthenticationException("token失效,请重新登录!");
}
return new SimpleAuthenticationInfo(token, token, getName());
}
}
4.涉及到的工具类
/**
* 鉴权登录拦截器
*
*
*/
public class JwtFilter extends BasicHttpAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(JwtFilter.class);
private static String AUTHORIZATION = "Authorization";
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(AUTHORIZATION);
String loginType = JwtUtil.getLoginType(token);
JwtToken jwtToken = new JwtToken(token,loginType);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginRequest(request, response)) {
try {
return super.onAccessDenied(request, response);
} catch (Exception e) {
logger.error("onAccessDenied Exception", e);
return false;
}
}
try {
// 接口需要登录
return executeLogin(request, response);
} catch (Exception e) {
return false;
}
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//访问拒绝的逻辑
if (isLoginRequest(request, response)) {
return false;
}
// 未登录
Subject subject = getSubject(request, response);
if (subject == null || subject.getPrincipal() == null) {
WebUtils.responseJsonText(response, ApiResponse.of("401", "un login"));
return false;
}
return super.onAccessDenied(request, response);
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
ServletResponse response) {
logger.warn("onLoginFailure AuthenticationToken={},AuthenticationException={}", token);
try {
WebUtils.responseJsonText(response, ApiResponse.of("500", e.getMessage()));
} catch (IOException e1) {
logger.error("response error", e);
return false;
}
return false;
}
@Override
protected String getHost(ServletRequest request) {
return WebUtils.getHost(request);
}
}
public class JwtUtil {
public static final String SID = "sid";
public static final String LOGIN_ACCOUNT = "loginAccount";
/**
* 机构id
*/
public static final String ORGANIZATION_ID = "organizationId";
/**
* 登录类型
*/
public static final String LOGIN_TYPE = "loginType";
/**
* 登录昵称
*/
public static final String LOGIN_NICKNAME = "loginNickname";
/**
* 校验token是否正确
*
* @param token 密钥
* @param loginAccount 登录账号
* @return 是否正确
*/
public static boolean verify(String token, String loginAccount) {
try {
Long userId = getUserId(token);
Algorithm algorithm = Algorithm.HMAC256(Md5Utils.md5Hex(userId.toString()));
JWTVerifier verifier = JWT.require(algorithm).withClaim(LOGIN_ACCOUNT, loginAccount).build();
verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getLoginAccount(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(LOGIN_ACCOUNT).asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 获得token中的信息无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getLoginNickname(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(LOGIN_NICKNAME).asString();
} catch (JWTDecodeException e) {
return null;
}
}
public static Long getUserId(String token) {
if(StringUtils.isEmpty(token)) {
return null;
}
try {
DecodedJWT jwt = JWT.decode(token);
return Long.parseLong(jwt.getSubject());
} catch (JWTDecodeException e) {
return null;
}
}
public static String getSid(String token) {
if(StringUtils.isEmpty(token)) {
return null;
}
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(SID).asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 后台管理员生成签名
* @param loginAccount 用户名
* @return 加密的token
*/
public static String signAdmin(String loginAccount,Long userId,String sid,String loginType,String nickName) {
try {
loginAccount = StringUtils.lowerCase(loginAccount);
Algorithm algorithm = Algorithm.HMAC256(Md5Utils.md5Hex(userId.toString()));
return JWT.create()
.withClaim(LOGIN_ACCOUNT, loginAccount)
.withClaim(SID, sid)
.withClaim(LOGIN_TYPE, loginType)
.withClaim(LOGIN_NICKNAME,nickName)
.withSubject(String.valueOf(userId))
.withExpiresAt(new Date(System.currentTimeMillis() + CacheExpiresTime.ADMIN_EXPIRE_TIME_MILLISECOND))
.sign(algorithm);
} catch (Exception e) {
return null;
}
}
public static String getLoginType(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(LOGIN_TYPE).asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
*
* title: 获取当前登录用户的机构id
* @param token
* @return
*/
public static Long getOrganizationId(String token) {
if(StringUtils.isEmpty(token)) {
return null;
}
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(ORGANIZATION_ID).asLong();
} catch (JWTDecodeException e) {
return null;
}
}
}