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 {@Beanpublic 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");}@Beanpublic DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 多realmSet<Realm> realms = new HashSet<>(MediaConstants.DEFAULT_COLLECTION_SIZE);realms.add(adminRealm());securityManager.setRealms(realms);// 关闭sessionDefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();sessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);securityManager.setSubjectDAO(subjectDAO);securityManager.setAuthenticator(authenticator());return securityManager;}@Beanpublic Authenticator authenticator() {ModularRealm modularRealm = new ModularRealm();modularRealm.setRealms(Arrays.asList(adminRealm()));modularRealm.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());return modularRealm;}@Beanpublic 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;}@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}
3.配置Realm
Realm中主要涉及用户身份验证和授权策略信息的设置
@Data
public class AdminShiroRealm extends AuthorizingRealm {@Autowiredprivate UserAuthService userAuthMgr;/*** 使用JWT替代原生Token** @param token* @return*/@Overridepublic 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 权限信息*/@Overrideprotected 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 实例*/@Overrideprotected 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";@Overrideprotected 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);// 如果没有抛出异常则代表登入成功,返回truereturn true;}@Overrideprotected 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;}}@Overrideprotected 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);}@Overrideprotected 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;}@Overrideprotected 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;}}}