springboot整合security实现权限控制
1.建表,五张表,如下:
1.1.用户表
CREATE TABLE `t_sys_user` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(30) NOT NULL COMMENT '用户名',
`user_password` varchar(128) NOT NULL COMMENT '用户密码',
`salt` varchar(64) DEFAULT NULL COMMENT '加密盐',
`user_phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`user_emai` varchar(20) DEFAULT NULL COMMENT '邮箱',
`user_title` varchar(20) DEFAULT NULL COMMENT '职称',
`creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
`creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
`updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
`updater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`role_ids` varchar(200) DEFAULT NULL,
`role_names` varchar(300) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
1.2.用户角色表
CREATE TABLE `t_sys_user_role` (
`user_role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户角色ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
1.3.角色表
CREATE TABLE `t_sys_role` (
`role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(100) NOT NULL COMMENT '角色名称',
`role_code` varchar(100) NOT NULL COMMENT '角色编码',
`creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
`creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
`updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
`updater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`permission_ids` varchar(200) DEFAULT NULL,
`permission_names` varchar(300) DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
1.4.角色权限表
CREATE TABLE `t_sys_role_permission` (
`role_permission_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色权限ID',
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
`permission_id` bigint(20) NOT NULL COMMENT '权限ID',
PRIMARY KEY (`role_permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8;
1.5.权限表
CREATE TABLE `t_sys_permission` (
`permission_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`permission_name` varchar(100) NOT NULL COMMENT '权限名称',
`permission_code` varchar(100) NOT NULL COMMENT '权限编码',
`creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
`creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
`updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
`updater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;
2.pom.xml引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.编码步骤:
3.1.在用户实体类中实现UserDetails接口的方法
package com.lz.hehuorenservice.system.entity; import com.lz.hehuorenservice.common.entity.BaseEntity; import io.swagger.annotations.ApiModelProperty; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.*; /** Create by hyhweb on 2021/6/6 16:24 */ public class User extends BaseEntity implements UserDetails { /** 用户主键ID */ @ApiModelProperty(value = "用户主键ID") private Long userId; /** 用户名 */ @ApiModelProperty(value = "用户名") private String userName; /** 用户密码 */ @ApiModelProperty(value = "用户密码") private String userPassword; @ApiModelProperty(value = "") private String salt; /** 手机号 */ @ApiModelProperty(value = "手机号") private String userPhone; /** 邮箱 */ @ApiModelProperty(value = "邮箱") private String userEmai; /** 职称 */ @ApiModelProperty(value = "职称") private String userTitle; @ApiModelProperty(value = "角色ID") private String roleIds; @ApiModelProperty(value = "角色名称") private String roleNames; /** 创建人ID */ @ApiModelProperty(value = "创建人ID") private Long createrId; /** 创建人名称 */ @ApiModelProperty(value = "创建人名称") private String createrName; /** 创建时间 */ @ApiModelProperty(value = "创建时间") private Date createrTime; /** 更新人ID */ @ApiModelProperty(value = "更新人ID") private Long updaterId; /** 更新人名称 */ @ApiModelProperty(value = "更新人名称") private String updaterName; /** 更新时间 */ @ApiModelProperty(value = "更新时间") private Date updaterTime; private Set<String> permissions; @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); /* //绑定角色的授权方法 if(roles !=null){ for (Role sysRole : roles) { authorities.add(new SimpleGrantedAuthority(sysRole.getRoleCode())); } }*/ // 绑定权限的授权方法 if (permissions != null) { for (String permission : permissions) { authorities.add(new SimpleGrantedAuthority(permission)); } } return authorities; } @Override public String getPassword() { return userPassword; } @Override public String getUsername() { return userName; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; } public String getUserPhone() { return userPhone; } public void setUserPhone(String userPhone) { this.userPhone = userPhone; } public String getUserEmai() { return userEmai; } public void setUserEmai(String userEmai) { this.userEmai = userEmai; } public String getUserTitle() { return userTitle; } public void setUserTitle(String userTitle) { this.userTitle = userTitle; } public String getRoleIds() { return roleIds; } public void setRoleIds(String roleIds) { this.roleIds = roleIds; } public String getRoleNames() { return roleNames; } public void setRoleNames(String roleNames) { this.roleNames = roleNames; } public Long getCreaterId() { return createrId; } public void setCreaterId(Long createrId) { this.createrId = createrId; } public String getCreaterName() { return createrName; } public void setCreaterName(String createrName) { this.createrName = createrName; } public Date getCreaterTime() { return createrTime; } public void setCreaterTime(Date createrTime) { this.createrTime = createrTime; } public Long getUpdaterId() { return updaterId; } public void setUpdaterId(Long updaterId) { this.updaterId = updaterId; } public String getUpdaterName() { return updaterName; } public void setUpdaterName(String updaterName) { this.updaterName = updaterName; } public Date getUpdaterTime() { return updaterTime; } public void setUpdaterTime(Date updaterTime) { this.updaterTime = updaterTime; } public Set<String> getPermissions() { return permissions; } public void setPermissions(Set<String> permissions) { this.permissions = permissions; } }
3.2.在用户的服务实现类中,实现UserDetailsService接口的loadUserByUsername方法,返回用户的所有信息。
package com.lz.hehuorenservice.system.service.impl; import com.lz.hehuorenservice.common.service.impl.BaseServiceImpl; import com.lz.hehuorenservice.system.dao.UserDao; import com.lz.hehuorenservice.system.entity.User; import com.lz.hehuorenservice.system.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.Set; /** Create by hyhweb on 2021/6/6 16:28 */ @Service public class UserServiceImpl extends BaseServiceImpl<User, Long> implements UserService, UserDetailsService { @Autowired UserDao userDao; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { User user = userDao.getUserByName(userName); if (user == null) { throw new UsernameNotFoundException("账户不存在"); } Set<String> permissions = userDao.getPermissionByUserId(user.getUserId()); user.setPermissions(permissions); return user; } }
3.3.编写配置类,重写WebSecurityConfigurerAdapter类的三个configure方法,也就是重新配置三个对象AuthenticationManagerBuilder,HttpSecurity,WebSecurity。
package com.lz.hehuorenservice.common.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.lz.hehuorenservice.common.bean.CustomAccessDeniedHandler; import com.lz.hehuorenservice.common.bean.CustomAuthenticationEntryPoint; import com.lz.hehuorenservice.common.filter.CustomAuthenticationFilter; import com.lz.hehuorenservice.system.entity.User; import com.lz.hehuorenservice.system.service.impl.UserServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.*; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.web.cors.CorsUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** Create by hyhweb on 2021/6/7 8:26 */ @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserServiceImpl userService; // 这个必须是接口的实现类,不能是接口 @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(10); // return NoOpPasswordEncoder.getInstance(); } /* @Bean RoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); // String hierarchy = "ROLE_dba> ROLE_admin \n ROLE_admin > ROLE_user"; String hierarchy = "ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierarchy; }*/ @Bean CustomAuthenticationFilter customAuthenticationFilter() throws Exception { CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); filter.setAuthenticationSuccessHandler( new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess( HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException { Object principal = auth.getPrincipal(); resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); resp.setStatus(200); Map<String, Object> map = new HashMap<>(); map.put("code", "1"); map.put("success", true); map.put("message", "登录成功"); User user = (User) principal; user.setUserPassword(null); map.put("data", user); ObjectMapper om = new ObjectMapper(); out.write(om.writeValueAsString(map)); out.flush(); out.close(); /* resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); Map<String,Object> map = new HashMap<String,Object>(); map.put("message", "登录成功"); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close();*/ } }); filter.setAuthenticationFailureHandler( new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure( HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException { resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); resp.setStatus(401); Map<String, Object> map = new HashMap<>(); map.put("status", 401); if (e instanceof LockedException) { map.put("msg", "账号被锁定,登录失败"); } else if (e instanceof BadCredentialsException) { map.put("msg", "账号或密码输入错误,请重新登录"); } else if (e instanceof DisabledException) { map.put("msg", "账号被禁用,登录失败"); } else if (e instanceof AccountExpiredException) { map.put("msg", "账号过期,登录失败"); } else if (e instanceof CredentialsExpiredException) { map.put("msg", "密码过期,登录失败"); } else { map.put("msg", "登录失败"); } ObjectMapper om = new ObjectMapper(); out.write(om.writeValueAsString(map)); out.flush(); out.close(); /*resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); Map<String,Object> map = new HashMap<String,Object>(); map.put("message", "登录失败"); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close();*/ } }); filter.setAuthenticationManager(authenticationManagerBean()); return filter; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService); } @Bean public AccessDeniedHandler getAccessDeniedHandler() { return new CustomAccessDeniedHandler(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers("/sessionInvalid", "/register", "/app/**", "/login_page") .antMatchers("/index.html", "/static/**", "/favicon.ico") .antMatchers( "/swagger-ui/**", "/swagger/**", "/doc.html", "/swagger-resources/**", "/images/**", "/webjars/**", "/v3/api-docs", "/configuration/ui", "/configuration/security"); } @Override protected void configure(HttpSecurity http) throws Exception { http.cors() // 开启跨域 .and() // 获取一个安全编译器 .authorizeRequests() // 授权请求 .requestMatchers(CorsUtils::isPreFlightRequest) .permitAll() // 跨域的请求开放所有权限 .anyRequest() // 所有请求 .authenticated() // 所有请求都需要认证 .and() .sessionManagement() .invalidSessionUrl("/session/invalid") .and() // 获取一个安全编译器 .formLogin() // 表单登录配置 .loginPage("/login_page") // 登录页面访问地址 .loginProcessingUrl("/login") // 配置登录接口地址 .usernameParameter("userName") // 配置登录的账号字段 .passwordParameter("userPassWord") // 配置登录密码字段 .and() // 获取一个安全编译器 .logout() // 退出登录配置 .logoutUrl("/logout") // 设置退出登录的接口地址 .clearAuthentication(true) // 清除所有认证信息 .invalidateHttpSession(true) // 让session失效 .addLogoutHandler( new LogoutHandler() { // 退出登录时的处理器 @Override public void logout( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {} }) .logoutSuccessHandler( new LogoutSuccessHandler() { // 退出成功后的处理器 @Override public void onLogoutSuccess( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=utf-8"); PrintWriter out = httpServletResponse.getWriter(); Map<String, Object> map = new HashMap<>(); map.put("message", "退出成功"); map.put("code", "1"); map.put("success", true); ObjectMapper om = new ObjectMapper(); out.write(om.writeValueAsString(map)); out.flush(); out.close(); } }) .permitAll() // 设置退出登录的所有权限 .and() // 获取一个安全编译器 .csrf() .disable() // 关闭csrf跨站点请求伪造 .exceptionHandling() .authenticationEntryPoint(new CustomAuthenticationEntryPoint()); // 自定义认证的入口异常处理方法 http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 重写用户名密码的过滤器,实现前后端分离获取登录的用户名,密码信息 http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler()); // 没有权限访问的处理器 } }
3.3.1CustomAccessDeniedHandler自定义没权限方法的处理器
package com.lz.hehuorenservice.common.bean; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** Create by hyhweb on 2021/6/7 11:50 */ public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=utf-8"); PrintWriter out = httpServletResponse.getWriter(); Map map = new HashMap<>(); map.put("message", "权限不足,请联系管理员开通权限"); map.put("code", 0); map.put("status", 403); map.put("success", false); String result = new ObjectMapper().writeValueAsString(map); out.write(result); out.flush(); out.close(); } } 3.3.2CustomAuthenticationEntryPoint自定义认证的入口 package com.lz.hehuorenservice.common.bean; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** Create by hyhweb on 2021/6/7 11:42 */ public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=utf-8"); PrintWriter out = httpServletResponse.getWriter(); Map map = new HashMap<>(); map.put("message", "还没登录,请重新登录"); map.put("code", 302); String result = new ObjectMapper().writeValueAsString(map); out.write(result); out.flush(); out.close(); } }
3.3.3.CustomAuthenticationFilter自定义
package com.lz.hehuorenservice.common.filter; import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; /** Create by hyhweb on 2021/6/7 12:07 */ public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Override public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) { UsernamePasswordAuthenticationToken authRequest = null; try (InputStream is = request.getInputStream()) { ObjectMapper mapper = new ObjectMapper(); Map<String, String> authenticationBean = mapper.readValue(is, Map.class); authRequest = new UsernamePasswordAuthenticationToken( authenticationBean.get("userName"), authenticationBean.get("userPassWord")); /* authRequest = new UsernamePasswordAuthenticationToken( request.getParameter("userName"), request.getParameter("userPassWord"));*/ } catch (IOException e) { e.printStackTrace(); authRequest = new UsernamePasswordAuthenticationToken("", ""); } finally { setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } } else { return super.attemptAuthentication(request, response); } } }
4.controller层使用权限注释@PreAuthorize实现权限控制
@RestController @RequestMapping("/user") @Api(tags = "用户信息") public class UserController{ @Autowired private UserService userService; @ApiOperation(value = "删除单个对象", notes = "删除单个对象接口") @GetMapping("/delete/{id}") @PreAuthorize("hasAuthority('delete')") public ApiResult deleteById(@PathVariable long id) { return userService.deleteById(id); } }
附加说明:
Spring Security的表达式对象的基类:
org.springframework.security.access.expression.SecurityExpressionRoot
在controller的方法中使用注释,如下:
@PreAuthorize(“表达式(‘权限值’)”)
@PreAuthorize("hasAuthority('zixunguanli-xinzeng')")
public ApiResult add(@RequestBody String json) {
return infoService.add(JSON.parseObject(json, InfoReq.class));
}
表达式如下:
boolean hasAuthority(String var1);
boolean hasAnyAuthority(String... var1);
boolean hasRole(String var1);
boolean hasAnyRole(String... var1);
boolean permitAll();
boolean denyAll();
boolean isAnonymous();
boolean isAuthenticated();
boolean isRememberMe();
boolean isFullyAuthenticated();
boolean hasPermission(Object var1, Object var2);
boolean hasPermission(Object var1, String var2, Object var3);
Spring Security的重构获取用户名和密码的方式,实现前后端分离的json格式,如下:
重构org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter的attemptAuthentication方法
spring boot集成spring security(jwt+redis有完整源码)
一、Spring Security官方解释
Spring Security是一个强大的和高度可定制的身份验证和访问控制框架。它是保证基于spring的应用程序安全的实际标准。Spring Security是一个框架,着重于为Java应用程序提供身份验证和授权。春天像所有项目,Spring Security的真正力量是很容易找到的它可以扩展以满足定制需求。
本项目使用jwt当作token,使用redis存储token,登录信息不依赖于单个项目,集中存储到redis内存型数据库中。
二、spring boot集成步骤(完整项目地址:https://gitee.com/fds520/spring-security-demo)
1. 项目结构
2.核心代码
主配置类SpringSecurityConfig
package com.fds.system.config; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; 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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * spring security 主配置类 */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired private SecurityProperties securityProperties; @Override protected void configure(HttpSecurity http) throws Exception { // 禁用 csrf 拦截 http.csrf().disable() .sessionManagement() // 关闭session管理,使用token机制处理 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // 未登录返回 JSON 格式的数据 .httpBasic().authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> { httpServletRequest.setCharacterEncoding("utf-8"); httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8"); httpServletResponse.getWriter().write(JSON.toJSONString("未登录")); }) .and() // 无权访问 JSON 格式的数据 .exceptionHandling().accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> { httpServletResponse.setCharacterEncoding("utf-8"); httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8"); httpServletResponse.getWriter().write(JSON.toJSONString("没有权限")); }) .and() .authorizeRequests() // 对option不校验 .antMatchers(HttpMethod.OPTIONS).permitAll() // 设置不校验白名单 .antMatchers(securityProperties.getIgnoreUrl()).permitAll() .anyRequest().authenticated() .and() // 添加自定义请求jwt过滤器 .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Override public void configure(WebSecurity web) { // 设置拦截忽略文件夹,可以对静态资源放行 web.ignoring().antMatchers("/images/**"); } }
登录service
/** * 获取数据库用户 * * @param username 用户名 * @return * @throws UsernameNotFoundException */ public String login(String username, String password) { // 模拟从数据库 获取登录用户 LoginUser loginUser = new LoginUser("fds", "123"); loginUser.setId(123L); loginUser.setType("people"); Set authoritiesSet = new HashSet(); // 模拟从数据库中获取用户权限 authoritiesSet.add("test:add"); authoritiesSet.add("test:list"); authoritiesSet.add("ddd:list"); loginUser.setCustomAuthorities(authoritiesSet); String token = JwtTokenUtil.generateToken(loginUser); redisUtil.set(token, JSONObject.toJSONString(loginUser), securityProperties.getExpirationMilliSeconds()); return token; }
token过滤器
package com.fds.system.config; import com.alibaba.fastjson.JSONObject; import com.fds.system.model.LoginUser; import com.fds.system.utils.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author: fds * @description: jwt-token过滤器 */ @Component @Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private SecurityProperties securityProperties; @Autowired private RedisUtil redisUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authToken = request.getHeader("Authorization"); response.setCharacterEncoding("utf-8"); if (StringUtils.isEmpty(authToken)) { // 用户未登录 filterChain.doFilter(request, response); return; } // 获取redis中的token信息 if (!redisUtil.hasKey(authToken)) { // 用户未登录 filterChain.doFilter(request, response); return; } Object data = redisUtil.get(authToken); if (null == data) { // 用户未登录 filterChain.doFilter(request, response); return; } // 获取缓存中的信息(根据自己的业务进行拓展) LoginUser loginUser = JSONObject.parseObject(data.toString(), LoginUser.class); // 设置权限 loginUser.setSystemAuthorities(); // 从tokenInfo中取出用户信息 // 更新token过期时间 redisUtil.setKeyExpire(authToken, securityProperties.getExpirationMilliSeconds()); // 将信息交给security UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); filterChain.doFilter(request, response); } }