diff --git a/pom.xml b/pom.xml index 0d7f637..51c171e 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,12 @@ <artifactId>ojdbc6</artifactId> <version>11.2.0.4.0-atlassian-hosted</version> </dependency> + <!--分页插件--> + <dependency> + <groupId>com.github.pagehelper</groupId> + <artifactId>pagehelper-spring-boot-starter</artifactId> + <version>1.2.5</version> + </dependency> <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --> <dependency> <groupId>org.dom4j</groupId> diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..52b1c72 --- /dev/null +++ b/readme.md @@ -0,0 +1,20 @@ +**gitlab**: [http://118.31.66.166:zp260/imf_cloud_wearhouse.git](git@118.31.66.166:zp260/imf_cloud_wearhouse.git) + +# 项目描述 +国际货运物流平台开发脚手架 +#集成 +* 已集成IMF,基于IMF的xml报文格式。在master的git主分支上 + * 接收IMF的消息 + * 报文类型识别 + * 报文分类本地存储 + * 报文发送 +* 已集成spring SECURITY + * 支持前后端分离 + * 自定义权限角色管理 + * url角色权限识别 + * menu与权限关联 +* 已集成mybatis、mybatisGenerator、pageHelper +* 集成定时任务框架 + * 目前在IMF框架中使用 +* 集成Spring Cloud + diff --git a/src/main/java/com/tianbo/warehouse/controller/AdminController.java b/src/main/java/com/tianbo/warehouse/controller/AdminController.java index 4ee986e..1e377ca 100644 --- a/src/main/java/com/tianbo/warehouse/controller/AdminController.java +++ b/src/main/java/com/tianbo/warehouse/controller/AdminController.java @@ -3,6 +3,9 @@ package com.tianbo.warehouse.controller; import com.tianbo.warehouse.model.USERS; import com.tianbo.warehouse.service.UserService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -15,7 +18,9 @@ public class AdminController { UserService userService; @GetMapping("/admin") - public List<USERS> admin(){ - return userService.selectAllUser(); + public String admin(){ + return "admin"; } + + } diff --git a/src/main/java/com/tianbo/warehouse/controller/ImfLog.java b/src/main/java/com/tianbo/warehouse/controller/ImfLog.java index cea8f7d..fe9b05a 100644 --- a/src/main/java/com/tianbo/warehouse/controller/ImfLog.java +++ b/src/main/java/com/tianbo/warehouse/controller/ImfLog.java @@ -34,9 +34,8 @@ public class ImfLog { @RequestMapping("/logs") @ResponseBody - public List<USERS> logs(){ - List<USERS> usersList =userService.selectAllUser(); - return usersList; + public String logs(){ + return "logs"; } } diff --git a/src/main/java/com/tianbo/warehouse/controller/Login.java b/src/main/java/com/tianbo/warehouse/controller/Login.java index 023001a..309cc00 100644 --- a/src/main/java/com/tianbo/warehouse/controller/Login.java +++ b/src/main/java/com/tianbo/warehouse/controller/Login.java @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @Controller public class Login { - @RequestMapping("/login") + @RequestMapping("/loginPage") public String login(){ return "login"; } diff --git a/src/main/java/com/tianbo/warehouse/controller/MainController.java b/src/main/java/com/tianbo/warehouse/controller/MainController.java index 031a78e..13261fe 100644 --- a/src/main/java/com/tianbo/warehouse/controller/MainController.java +++ b/src/main/java/com/tianbo/warehouse/controller/MainController.java @@ -20,8 +20,7 @@ public class MainController { } @GetMapping("/main") - public List<USERS> me(){ - List<USERS> usersList =userService.selectAllUser(); - return usersList; + public String main(){ + return "main"; } } diff --git a/src/main/java/com/tianbo/warehouse/controller/UserController.java b/src/main/java/com/tianbo/warehouse/controller/UserController.java new file mode 100644 index 0000000..8e0037b --- /dev/null +++ b/src/main/java/com/tianbo/warehouse/controller/UserController.java @@ -0,0 +1,35 @@ +package com.tianbo.warehouse.controller; + +import com.github.pagehelper.PageInfo; +import com.tianbo.warehouse.model.USERS; +import com.tianbo.warehouse.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class UserController { + + @Autowired + UserService userService; + + @GetMapping("/user/list") + public PageInfo<USERS> list(@RequestParam(value = "pageNum",required = false,defaultValue = "1") + int pageNum, + @RequestParam(value = "pageSize",required = false,defaultValue = "10") + int pageSize){ + return userService.selectAllUser(pageNum,pageSize); + } + + public String getusername(){ + + //通过session获取当前登录的用户信息 + UserDetails userDetails =(UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + return "欢迎回来:"+userDetails.getUsername(); + } +} diff --git a/src/main/java/com/tianbo/warehouse/security/MyAccessDecisionManager.java b/src/main/java/com/tianbo/warehouse/security/MyAccessDecisionManager.java index cf4d806..339f190 100644 --- a/src/main/java/com/tianbo/warehouse/security/MyAccessDecisionManager.java +++ b/src/main/java/com/tianbo/warehouse/security/MyAccessDecisionManager.java @@ -13,7 +13,7 @@ import java.util.Iterator; @Service public class MyAccessDecisionManager implements AccessDecisionManager{ - /** + /**这里没用AccessDecisionVoter访问投票管理,自定义用户的role_name与URL需要的ROLE_NAME对碰决定,参考资料:https://blog.csdn.net/kaikai8552/article/details/3965841 * decide方法接收三个参数,decide 方法是判定是否拥有权限的决策方法 * 其中第一个参数中保存了当前登录用户的角色信息,authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合. * object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); @@ -35,6 +35,12 @@ public class MyAccessDecisionManager implements AccessDecisionManager{ for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) { c = iter.next(); needRole = c.getAttribute(); + + //如果URL需要的权限为匿名访问,返回 + if(("ROLE_ANONYMOUS").equals(needRole.trim())){ + return; + } + //authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合 for(GrantedAuthority ga : authentication.getAuthorities()) { if(needRole.trim().equals(ga.getAuthority())) { diff --git a/src/main/java/com/tianbo/warehouse/security/MyInvocationSecurityMetadataSourceService.java b/src/main/java/com/tianbo/warehouse/security/MyInvocationSecurityMetadataSourceService.java index 9150aef..67631f4 100644 --- a/src/main/java/com/tianbo/warehouse/security/MyInvocationSecurityMetadataSourceService.java +++ b/src/main/java/com/tianbo/warehouse/security/MyInvocationSecurityMetadataSourceService.java @@ -57,6 +57,8 @@ public class MyInvocationSecurityMetadataSourceService implements FilterInvocati * 此方法是为了判定用户请求的url 是否在权限表中, * 如果在权限表中,则返回给 decide 方法, * 用来判定用户是否有此权限。如果不在权限表中则放行。 + * 如果getAttributes(Object o)方法返回null的话,意味着当前这个请求不需要任何角色就能访问 + * getAttributes(Object o)方法返回的集合最终会来到AccessDecisionManager类中 */ @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { diff --git a/src/main/java/com/tianbo/warehouse/security/config/CorsConfig.java b/src/main/java/com/tianbo/warehouse/security/config/CorsConfig.java new file mode 100644 index 0000000..3867551 --- /dev/null +++ b/src/main/java/com/tianbo/warehouse/security/config/CorsConfig.java @@ -0,0 +1,20 @@ +package com.tianbo.warehouse.security.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +/** + * 解决前后端分离跨域问题 + */ +@Configuration +public class CorsConfig implements WebMvcConfigurer{ + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**")//设置允许跨域的路径 + .allowedOrigins("*")//设置允许跨域请求的域名 + .allowCredentials(true)//是否允许证书 不再默认开启 + .allowedMethods("GET", "POST", "PUT", "DELETE")//设置允许的方法 + .maxAge(3600);//跨域允许时间 + } +} diff --git a/src/main/java/com/tianbo/warehouse/security/config/WebSecurityConfig.java b/src/main/java/com/tianbo/warehouse/security/config/WebSecurityConfig.java index d9f4de1..1ca7709 100644 --- a/src/main/java/com/tianbo/warehouse/security/config/WebSecurityConfig.java +++ b/src/main/java/com/tianbo/warehouse/security/config/WebSecurityConfig.java @@ -1,21 +1,29 @@ package com.tianbo.warehouse.security.config; +import com.netflix.discovery.converters.Auto; +import com.tianbo.warehouse.security.handel.MyAuthenticationAccessDeniedHandler; import com.tianbo.warehouse.security.handel.MyAuthenticationFailHandler; import com.tianbo.warehouse.security.handel.MyAuthenticationSuccessHandler; import com.tianbo.warehouse.security.MyFilterSecurityInterceptor; +import com.tianbo.warehouse.security.handel.MyLogoutSuccessHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.web.cors.CorsUtils; @Configuration @EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) @Order(1) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -35,18 +43,36 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; + @Autowired + private MyAuthenticationAccessDeniedHandler myAuthenticationAccessDeniedHandler; + + @Autowired + private MyLogoutSuccessHandler myLogoutSuccessHandler; + @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //user Details Service验证 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } + /** + * 在configure(HttpSecurity http)方法中, + * 通过withObjectPostProcessor将刚刚创建的UrlFilterInvocationSecurityMetadataSource和UrlAccessDecisionManager注入进来。 + * 到时候,请求都会经过刚才的过滤器(除了configure(WebSecurity web)方法忽略的请求)。 + * 通过myFilterSecurityInterceptor关联他俩 + * @param http + * @throws Exception + */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() + //跨域配置 + .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() + //管理页面只允许管理员角色访问 .antMatchers("/admin","/role").authenticated() - //管理页面只允许管理员角色访问 //任何请求,登录后可以访问 - .anyRequest().permitAll() //其余的不需要验证 + //任何请求,登录后可以访问 + //其余的不需要验证 + .anyRequest().permitAll() .and() .formLogin() .passwordParameter("password") @@ -61,8 +87,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .permitAll() // .successForwardUrl("/main") .and() + .exceptionHandling().accessDeniedHandler(myAuthenticationAccessDeniedHandler) + .and() .logout() .logoutSuccessUrl("/?logout=true") + .logoutSuccessHandler(myLogoutSuccessHandler) .permitAll() .and() .rememberMe() @@ -77,6 +106,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .and() .csrf().disable(); - //http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class).csrf().disable(); + http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class); } } diff --git a/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationAccessDeniedHandler.java b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationAccessDeniedHandler.java new file mode 100644 index 0000000..3bbaed0 --- /dev/null +++ b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationAccessDeniedHandler.java @@ -0,0 +1,31 @@ +package com.tianbo.warehouse.security.handel; + +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常 + * AccessDeineHandler 用来解决认证过的用户访问无权限资源时的异常 + */ +@Component +public class MyAuthenticationAccessDeniedHandler implements AccessDeniedHandler{ + + @Override + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse resp, AccessDeniedException e) throws IOException, ServletException{ + + resp.setStatus(HttpServletResponse.SC_FORBIDDEN); + resp.setCharacterEncoding("UTF-8"); + PrintWriter out = resp.getWriter(); + out.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员!\"}"); + out.flush(); + out.close(); + } + +} diff --git a/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationEntryPoint.java b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationEntryPoint.java new file mode 100644 index 0000000..2ba0dba --- /dev/null +++ b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationEntryPoint.java @@ -0,0 +1,14 @@ +package com.tianbo.warehouse.security.handel; + +import org.springframework.security.web.AuthenticationEntryPoint; + +/**实现AuthenticationEntryPoint接口 + * AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常 + * AccessDeineHandler 用来解决认证过的用户访问无权限资源时的异常 + */ +public class MyAuthenticationEntryPoint { +// response.setCharacterEncoding("utf-8"); +// response.setContentType("text/javascript;charset=utf-8"); +// response.getWriter().print(JSONObject.toJSONString(RestMsg.error("没有访问权限!"))); + +} diff --git a/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationFailHandler.java b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationFailHandler.java index 9cf5c8a..22d257a 100644 --- a/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationFailHandler.java +++ b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationFailHandler.java @@ -7,7 +7,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; @@ -17,6 +20,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.PrintWriter; /** * 自定义登录失败处理器 @@ -41,7 +45,25 @@ public class MyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureH @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + //返回前端原因 + PrintWriter out = response.getWriter(); + StringBuffer sb = new StringBuffer(); + sb.append("{\"status\":\"error\",\"msg\":\""); + if (exception instanceof UsernameNotFoundException || exception instanceof BadCredentialsException) { + sb.append("用户名或密码输入错误,登录失败!"); + } else if (exception instanceof DisabledException) { + sb.append("账户被禁用,登录失败,请联系管理员!"); + } else { + sb.append("登录失败!"); + } + sb.append("\"}"); +// out.write(sb.toString()); +// out.flush(); +// out.close(); + logger.info("登录失败"); + + //不返回具体原因 只返回异常 //如果securityProperties中配置的是JSON就返回JSON if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){ //设置状态码 diff --git a/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationSuccessHandler.java b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationSuccessHandler.java index eae111d..5be1a55 100644 --- a/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationSuccessHandler.java +++ b/src/main/java/com/tianbo/warehouse/security/handel/MyAuthenticationSuccessHandler.java @@ -41,6 +41,7 @@ public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticat if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){ //将 authention 信息打包成json格式返回 response.setContentType("application/json;charset=UTF-8"); + response.setHeader("Access-Control-Allow-Origin","*"); response.getWriter().write(objectMapper.writeValueAsString(authentication)); }else { //走原来的处理流程 diff --git a/src/main/java/com/tianbo/warehouse/security/handel/MyLogoutSuccessHandler.java b/src/main/java/com/tianbo/warehouse/security/handel/MyLogoutSuccessHandler.java new file mode 100644 index 0000000..7e4e99b --- /dev/null +++ b/src/main/java/com/tianbo/warehouse/security/handel/MyLogoutSuccessHandler.java @@ -0,0 +1,28 @@ +package com.tianbo.warehouse.security.handel; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@Component +public class MyLogoutSuccessHandler implements LogoutSuccessHandler { + + @Override + public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException{ + resp.setContentType("application/json;charset=utf-8"); +// RespBean respBean = RespBean.ok("注销成功!"); + ObjectMapper om = new ObjectMapper(); + PrintWriter out = resp.getWriter(); + out.write(om.writeValueAsString(authentication)); + out.flush(); + out.close(); + } + +} diff --git a/src/main/java/com/tianbo/warehouse/service/UserService.java b/src/main/java/com/tianbo/warehouse/service/UserService.java index 96805c8..0e756be 100644 --- a/src/main/java/com/tianbo/warehouse/service/UserService.java +++ b/src/main/java/com/tianbo/warehouse/service/UserService.java @@ -1,10 +1,11 @@ package com.tianbo.warehouse.service; +import com.github.pagehelper.PageInfo; import com.tianbo.warehouse.model.USERS; import java.util.List; public interface UserService { USERS loadByUsername(String username); - List<USERS> selectAllUser(); + PageInfo<USERS> selectAllUser(int pageNum, int pageSize); } diff --git a/src/main/java/com/tianbo/warehouse/service/imp/UserServiceImpl.java b/src/main/java/com/tianbo/warehouse/service/imp/UserServiceImpl.java index cb3a2b2..b540e3c 100644 --- a/src/main/java/com/tianbo/warehouse/service/imp/UserServiceImpl.java +++ b/src/main/java/com/tianbo/warehouse/service/imp/UserServiceImpl.java @@ -1,5 +1,8 @@ package com.tianbo.warehouse.service.imp; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; import com.tianbo.warehouse.dao.PERMISSIONMapper; import com.tianbo.warehouse.dao.ROLEMapper; import com.tianbo.warehouse.dao.USERSMapper; @@ -47,7 +50,8 @@ public class UserServiceImpl implements UserService{ } @Override - public List<USERS> selectAllUser(){ + public PageInfo<USERS> selectAllUser(int pageNum, int pageSize){ + Page<USERS> page = PageHelper.startPage(pageNum,pageSize); List<USERS> list = usersMapper.selectAllUser(); for (USERS user: list) { List<PERMISSION> permissionList = permissionMapper.findByUserId(user.getUserId()); @@ -55,6 +59,7 @@ public class UserServiceImpl implements UserService{ List<ROLE> roleList = roleMapper.findRolesByUserId(user.getUserId()); user.setRoles(roleList); } - return list; + PageInfo<USERS> result = new PageInfo<USERS>(list); + return result; } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d5e6389..2649aa8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,6 +8,9 @@ server.servlet.context-path=${SERVER_CONTEXTPATH:} spring.application.name=tianbo.base.dev.devkit spring.jackson.serialization.fail-on-empty-beans=false +#springboot2.0之后会把Date类型字段自动给转成UTC字符串 如:1990-11-26T16:00:00.000+0000,如果想转成时间戳在application.properties配置文件增加以下配置 +spring.jackson.serialization.write-dates-as-timestamps=true +spring.jackson.time-zone=GMT+8 #springcloud 基本配置