Springboot项目集成Shiro+JWT,同时集成swagger2

Springboot项目集成Shiro+JWT,同时集成swagger2便于测试
之前只是单独使用过Shiro和自定义token,后来觉得使用jwt更方便,所以集成做个笔记,直接按照步骤即插即用
1. 配置Maven

	
		org.apache.shiro
		shiro-core
		1.3.2
	
	
		org.apache.shiro
		shiro-spring
		1.3.2
	
	
		com.auth0
		java-jwt
		3.4.0
	

2. 创建JWTToken用于保存token

	/**
	 * 

* 自定义Token *

* * @author LiGuangYao * @data 2019/4/24 15:50 */ 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 token; } }

3. 创建JWT工具类

/**
 * 

* JWT工具类 *

* * @author LiGuangYao * @data 2019/4/24 15:51 */ public class JWTUtils { // 过期时间24 private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000; /** * 校验token是否正确 * * @param token 密钥 * @param username 登录名 * @param password 密码 * @return */ public static boolean verify(String token, String username, String password) { try { Algorithm algorithm = Algorithm.HMAC256(password); JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build(); DecodedJWT jwt = verifier.verify(token); return true; } catch (Exception e) { return false; } } /** * 获取登录名 * * @param token * @return */ public static String getUsername(String token) { try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("username").asString(); } catch (JWTDecodeException e) { return null; } } /** * 生成签名 * * @param username * @param password * @return */ public static String sign(String username, String password) { try { // 指定过期时间 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(password); return JWT.create() .withClaim("username", username) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { return null; } } }

4.自定义Shiro中的Realm

/**
 * 

* 自定义realm *

* * @author LiGuangYao * @data 2019/4/24 15:55 */ public class CustomRealm extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private ShiroAuthService shiroAuthService; /** * 权限验证 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { logger.info("doGetAuthorizationInfo+" + principals.toString()); String username = JWTUtils.getUsername(principals.toString()); User user = shiroAuthService.queryUserByName(username); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); List userPermissions = shiroAuthService.getPermissions(user.getUser_id()); // 基于Permission的权限信息 info.addStringPermissions(userPermissions); return info; } /** * 使用JWT替代原生Token * * @param token * @return */ @Override public boolean supports(AuthenticationToken token) { return token instanceof JWTToken; } /** * 登录验证 * @param authcToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { String token = (String) authcToken.getCredentials(); String username = JWTUtils.getUsername(token); User user = shiroAuthService.queryUserByName(username); // 用户不会空 if (user != null) { // 判断是否禁用 if (StringUtils.isBlank(UserForbidden.PROHIBIT.getMessage())) { throw new DisabledAccountException(BizException.USER_LOCK); } // 密码验证 if (!JWTUtils.verify(token, username, user.getPassword())) { throw new UnknownAccountException(BizException.USER_PASSWORD_EXISTENT); } return new SimpleAuthenticationInfo(token, token,Constant.CUSTOMREALM_NAME); } else { throw new UnknownAccountException(BizException.USER_EXISTENT); } } }

5. 自定义Shrio中的Filter

/**
 * 

* 自定义过滤器 *

* * @author LiGuangYao * @data 2019/4/24 15:53 */ public class JWTFilter extends AuthenticatingFilter { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { //获取请求token String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isBlank(token)){ return null; } return new JWTToken(token); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { return false; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //获取请求token,如果token不存在,直接返回401 String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isBlank(token)){ HttpServletResponse httpResponse = (HttpServletResponse) response; String json = new Gson().toJson( //自定义异常 需要自己创建或者填写 R.error(BizException.TOKEN_EXISTENT)); httpResponse.setContentType(Constant.RESPONSE_CONTENTTYPE); httpResponse.getWriter().print(json); return false; } return executeLogin(request, response); } @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setContentType(Constant.RESPONSE_CONTENTTYPE); try { //处理登录失败的异常 Throwable throwable = e.getCause() == null ? e : e.getCause(); R r = R.error(org.apache.http.HttpStatus.SC_UNAUTHORIZED, throwable.getMessage()); String json = new Gson().toJson(r); httpResponse.getWriter().print(json); } catch (IOException e1) { } return false; } /** * 获取请求的token */ private String getRequestToken(HttpServletRequest httpRequest){ //从header中获取token String token = httpRequest.getHeader(Constant.LOGIN_SIGN); //如果header中不存在token,则从参数中获取token if(StringUtils.isBlank(token)){ token = httpRequest.getParameter(Constant.LOGIN_SIGN); } return token; } }

6.自定义Shiro核心配置

/**
 * 

* Shiro核心配置 *

* * @author LiGuangYao * @data 2019/4/25 13:30 */ @Configuration public class ShiroConfig { /** * 先走 filter ,然后 filter 如果检测到请求头存在 token,则用 token 去 login,走 Realm 去验证 * * @param securityManager the security manager * @return the shiro filter factory bean */ @Bean public ShiroFilterFactoryBean factory(SecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); Map filterMap = new HashMap<>(); //设置我们自定义的JWT过滤器 filterMap.put("jwt", new JWTFilter()); factoryBean.setFilters(filterMap); factoryBean.setSecurityManager(securityManager); // 设置无权限时跳转的 url; factoryBean.setUnauthorizedUrl("/unauthorized/无权限"); Map filterRuleMap = new HashMap<>(); //访问/login和/unauthorized 不需要经过过滤器 filterRuleMap.put("/login", "anon"); filterRuleMap.put("/entry", "anon"); filterRuleMap.put("/unauthorized/**", "anon"); //swagger配置放行 filterRuleMap.put("/swagger-ui.html","anon"); filterRuleMap.put("/swagger/**","anon"); filterRuleMap.put("/webjars/**","anon"); filterRuleMap.put("/swagger-resources/**","anon"); filterRuleMap.put("/v2/**","anon"); //静态资源放行 filterRuleMap.put("/**/*.html","anon"); filterRuleMap.put("/**/*.jpg","anon"); filterRuleMap.put("/**/*.png","anon"); // 所有请求通过我们自己的JWT Filter filterRuleMap.put("/**", "jwt"); // 访问 /unauthorized/** 不通过JWTFilter factoryBean.setFilterChainDefinitionMap(filterRuleMap); return factoryBean; } /** * 注入 securityManager * * @return the security manager */ @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置自定义 realm. securityManager.setRealm(customRealm()); /* * 关闭shiro自带的session,详情见文档 * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29 */ DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); securityManager.setSubjectDAO(subjectDAO); return securityManager; } @Bean public CustomRealm customRealm() { return new CustomRealm(); } /** * 开启shiro aop注解支持. 使用代理方式; 所以需要开启代码支持; * * @param securityManager 安全管理器 * @return 授权Advisor */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } }

7.集成配置Swagger2 配置Maven

		
		
			io.springfox
			springfox-swagger2
			2.9.2
		

		
			io.springfox
			springfox-swagger-ui
			2.9.2
		
		
			io.swagger
			swagger-annotations
			1.5.21
		
		
			io.swagger
			swagger-models
			1.5.21
		

8.Swagger2中的yml加载project配置

	   project:
	    package:     //所属包名
	    name:         //swagger2展示名称
	    version: 1.0.0   //版本号
	    author:       
	    email:
	    site:

9.Swagger2核心配置

	/**
 * 

* Swagger2配置类 * 在与spring boot集成时,放在与Application.java同级的目录下。同时通过资源文件加载的方式便于修改 * 通过@Configuration注解,让Spring来加载该类配置。 * 再通过@EnableSwagger2注解来启用Swagger2。 *

* * @author LiGuangYao * @data 2019/4/8 14:19 */ @Configuration @EnableSwagger2 @ConditionalOnProperty(prefix = "swagger2",value = {"enable"},havingValue = "true") public class Swagger2 { @Value("${project.package}") String projectPackage; @Value("${project.name}") String projectName; @Value("${project.version}") String projectVersion; @Value("${project.author}") String projectAuthor; @Value("${project.email}") String projectEmail; @Value("${project.site}") String projectSite; /** * 创建API应用 * apiInfo() 增加API相关信息 * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现, * 本例采用指定扫描的包路径来定义指定要建立API的目录。 * * @return */ @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(String.format("%s.controller", projectPackage))) .paths(PathSelectors.any()) .build() .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } private static List securitySchemes() { List apiKeyList= new ArrayList();//x-auth-token //Constant.LOGIN_SIGN 为请求头中自定义的token名称 apiKeyList.add(new ApiKey(Constant.LOGIN_SIGN, Constant.LOGIN_SIGN, "header")); return apiKeyList; } private List securityContexts() { List securityContexts=new ArrayList<>(); securityContexts.add( SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex("^(?!auth).*$"))//^(?!auth).*$ .build()); return securityContexts; } List defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List securityReferences=new ArrayList<>(); securityReferences.add(new SecurityReference(Constant.LOGIN_SIGN, authorizationScopes)); return securityReferences; } /** * 创建该API的基本信息(这些基本信息会展现在文档页面中) * 访问地址:http://项目实际地址/swagger-ui.html * @return */ private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(String.format("%s API Documents", projectName)) .description("API Docs") .contact(new Contact(projectAuthor, projectSite, projectEmail)) .version(projectVersion) .build(); } }

你可能感兴趣的:(Springboot项目集成Shiro+JWT,同时集成swagger2)