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);
}
}
springboot整合security实现权限控制的更多相关文章
- SpringBoot整合Shiro实现权限控制,验证码
本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂. 目前我的需求是一个博客系统,有用户和管理员两种角色.一个用户可能有 ...
- SpringBoot整合Shiro实现权限控制
目录 1.SpringBoot整合Shiro 1.1.shiro简介 1.2.代码的具体实现 1.2.1.Maven的配置 1.2.2.整合需要实现的类 1.2.3.项目结构 1.2.4.ShiroC ...
- request.getRemoteUser() Spring Security做权限控制后
一. request.getRemoteUser();//获取当前缓存的用户,比如Spring Security做权限控制后就会将用户登录名缓存到这里 request.getRemoteAddr(); ...
- springboot整合security实现基于url的权限控制
权限控制基本上是任何一个web项目都要有的,为此spring为我们提供security模块来实现权限控制,网上找了很多资料,但是提供的demo代码都不能完全满足我的需求,因此自己整理了一版. 在上代码 ...
- springboot集成shiro 实现权限控制(转)
shiro apache shiro 是一个轻量级的身份验证与授权框架,与spring security 相比较,简单易用,灵活性高,springboot本身是提供了对security的支持,毕竟是自 ...
- springboot整合shiro进行权限管理
背景:springboot2.1,shiro1.4:由于目前的小项目没做登录,但是客户又需要加上权限,因此楼主就想到了shiro(这是单独的项目,需要集成后台管理系统) shiro简介 Apache ...
- SpringBoot + Security实现权限控制
网上找了好几个,因为各种原因不太行,下面这个亲测可行 参考:https://blog.csdn.net/u012702547/article/details/54319508 基于SpringBoot ...
- SpringBoot集成Shiro实现权限控制
Shiro简介 Apache Shiro是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理.使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移 ...
- SpringBoot整合Apache Shiro权限验证框架
比较常见的权限框架有两种,一种是Spring Security,另一种是Apache Shiro,两种框架各有优劣,个人感觉Shiro更容易使用,更加灵活,也更符合RABC规则,而且是java官方更推 ...
- shiro系列三、ssm框架整合shiro实现权限控制
shiro权限框架是一个非常优秀的框架,前面的几篇文章对shiro进行了非常详细的介绍和原理分析,那么接下来让我们开始在web项目中使用它(javase也能用shiro): 一.数据库表结构设计 二. ...
随机推荐
- TP6 使用 nusoap为第三方webservice调用插件
composer下载插件 composer require nusoap/nusoap use NuSoap\Client\Client; class Index extends BaseContro ...
- Spring-Event入门实践及执行原理
一.入门案例 1. 添加依赖首先,在 pom.xml 文件中添加 Spring Boot 和 Spring Event 的依赖: <dependencies> <dependency ...
- 告别繁琐的云平台开发!IoT_CLOUD之【百度云】
众所周知,市面上有很多云平台,阿里云.腾讯云.中移OneNET.华为云.百度云.涂鸦云.Tlink云等等......并且每家云平台都有自己的协议,工程师要移植不同的SDK代码或基于各家的手册文档对 ...
- 记录一个Linux代码移植到Windows平台下的Visual Studio 2022的代码编码格式的问题
一.前言 工作上与公司的前辈对接,他给了我一份在linux下面编写的代码压缩包,按照道理来说使用条件宏编译不同的windows和linux的API即可实现代码的通用.但是我在Visual Studio ...
- 【网关开发】Openresty使用cosocket API 发送http与tcp网络请求
背景 为网关提供健康检查功能时需要对节点发送http或者tcp探活请求.Openresty 提供cosocket来处理非阻塞IO. 实现 跟工程结合在一起,这里简单拼接数据结构 local funct ...
- java 中的Unsafe
在阅读AtomicInteger的源码时,看到了这个类:sum.msic.Unsafe,之前从没见过.所以花了点时间google了一下. Unsafe的源码:http://www.docjar.com ...
- Redis集群搭建-Docker
使用docker搭建本地集群Redis服务(参考:https://www.cnblogs.com/slowbirdoflsh/p/11633113.html) 环境配置 # 拉取redis镜像 doc ...
- ZCMU-1038
其实感觉不太难,读懂题意就行,我一开始没有仔细去读感觉就很懵.其题目意思就是一段字符串含有数字和'<'或者'>',一开始从左开始遍历,遇到'>'这类东西换方向,如果有多次遇到就删之前 ...
- MySql 9 in Docker 利用克隆插件搭建主从
环境说明 Docker Windows 11 MySql 9.1.0 搭建步骤 1. 准备主库 准备一个主库的配置文件 master.cnf [mysqld] server-id=1 log-bin= ...
- 前端必须知道的手机调试工具vConsole
在日常业务中我相信大家多多少少都有移动端的项目,移动端的项目需要真机调试的很多东西看不到调试起来也比较麻烦,今天给大家分享一个我认为比较好用的调试第三方库VConsole ,有了这个库咱们就在手机上看 ...