作者 朱兆平

完成security

... ... @@ -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>
... ...
**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
... ...
... ... @@ -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";
}
}
... ...
... ... @@ -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";
}
}
... ...
... ... @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Login {
@RequestMapping("/login")
@RequestMapping("/loginPage")
public String login(){
return "login";
}
... ...
... ... @@ -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";
}
}
... ...
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();
}
}
... ...
... ... @@ -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())) {
... ...
... ... @@ -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 {
... ...
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);//跨域允许时间
}
}
... ...
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);
}
}
... ...
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();
}
}
... ...
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("没有访问权限!")));
}
... ...
... ... @@ -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())){
//设置状态码
... ...
... ... @@ -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 {
//走原来的处理流程
... ...
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();
}
}
... ...
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);
}
... ...
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;
}
}
... ...
... ... @@ -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 基本配置
... ...