当前位置: 首页 > news >正文

Spring Security 快速开始

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

一、认证

1、从数据中读数据完成认证

@Service
public class MyUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws AuthenticationException {MyUser myUser;// 这里模拟从数据库中获取用户信息if (username.equals("admin")) {myUser = new MyUser("admin", new BCryptPasswordEncoder().encode("123456"), new ArrayList<>(Arrays.asList("p1", "p2")));return myUser;} else {throw new UsernameNotFoundException("用户不存在");}}
}

集成User可以补充一些自己的数据,修改构造方法(或者直接使用User)

public class MyUser extends User {private int sex;private int age;private String address;public MyUser(String username, String password, List<String> authorities) {super(username, password, authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}
}

相关配置

@Configuration
public class MySecurityConfigurer extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}}

2、实现同时多种认证方式

在1的基础上添加相关配置

/*** 手机验证码认证信息,在UsernamePasswordAuthenticationToken的基础上添加属性 手机号、验证码*/
public class MobilecodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = 530L;private Object principal;private Object credentials;private String phone;private String mobileCode;public MobilecodeAuthenticationToken(String phone, String mobileCode) {super(null);this.phone = phone;this.mobileCode = mobileCode;this.setAuthenticated(false);}public MobilecodeAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;this.credentials = credentials;super.setAuthenticated(true);}public Object getCredentials() {return this.credentials;}public Object getPrincipal() {return this.principal;}public String getPhone() {return phone;}public String getMobileCode() {return mobileCode;}public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {if (isAuthenticated) {throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");} else {super.setAuthenticated(false);}}public void eraseCredentials() {super.eraseCredentials();this.credentials = null;}
}
public class MobilecodeAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userDetailsService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {MobilecodeAuthenticationToken mobilecodeAuthenticationToken = (MobilecodeAuthenticationToken) authentication;String phone = mobilecodeAuthenticationToken.getPhone();String mobileCode = mobilecodeAuthenticationToken.getMobileCode();System.out.println("登陆手机号:" + phone);System.out.println("手机验证码:" + mobileCode);// 模拟从redis中读取手机号对应的验证码及其用户名Map<String,String> dataFromRedis = new HashMap<>();dataFromRedis.put("code", "6789");dataFromRedis.put("username", "admin");// 判断验证码是否一致if (!mobileCode.equals(dataFromRedis.get("code"))) {throw new BadCredentialsException("验证码错误");}// 如果验证码一致,从数据库中读取该手机号对应的用户信息MyUser loadedUser = (MyUser) userDetailsService.loadUserByUsername((String)dataFromRedis.get("username"));if (loadedUser == null) {throw new UsernameNotFoundException("用户不存在");} else {MobilecodeAuthenticationToken result = new MobilecodeAuthenticationToken(loadedUser, null, loadedUser.getAuthorities());return result;}}@Overridepublic boolean supports(Class<?> aClass) {return MobilecodeAuthenticationToken.class.isAssignableFrom(aClass);}public void setUserDetailsService(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}
}

修改配置文件

@Configuration
public class MySecurityConfigurer extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService myUserDetailsService;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic MobilecodeAuthenticationProvider mobilecodeAuthenticationProvider() {MobilecodeAuthenticationProvider mobilecodeAuthenticationProvider = new MobilecodeAuthenticationProvider();mobilecodeAuthenticationProvider.setUserDetailsService(myUserDetailsService);return mobilecodeAuthenticationProvider;}@Beanpublic DaoAuthenticationProvider daoAuthenticationProvider() {DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());daoAuthenticationProvider.setUserDetailsService(myUserDetailsService);return daoAuthenticationProvider;}/*** 定义认证管理器AuthenticationManager* @return*/@Beanpublic AuthenticationManager authenticationManager() {List<AuthenticationProvider> authenticationProviders = new ArrayList<>();authenticationProviders.add(mobilecodeAuthenticationProvider());authenticationProviders.add(daoAuthenticationProvider());ProviderManager authenticationManager = new ProviderManager(authenticationProviders);
//        authenticationManager.setEraseCredentialsAfterAuthentication(false);return authenticationManager;}@Overridepublic void configure(HttpSecurity http) throws Exception {http// 关闭csrf.csrf().disable()// 权限配置,登录相关的请求放行,其余需要认证.authorizeRequests().antMatchers("/login/*").permitAll().anyRequest().authenticated();}
}

发送登录请求

@RestController
@RequestMapping("/login")
public class TestController {@GetMapping("/hello")public String hello(){return "Hello Spring security";}@Autowiredprivate AuthenticationManager authenticationManager;/*** 用户名密码登录* @param username* @param password* @return*/@GetMapping("/usernamePwd")public Result usernamePwd(String username, String password) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);Authentication authenticate = null;try {authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);} catch (Exception e) {e.printStackTrace();return Result.error("登陆失败");}String token = UUID.randomUUID().toString().replace("-", "");return Result.ok(token);}/*** 手机验证码登录* @param phone* @param mobileCode* @return*/@GetMapping("/mobileCode")public Result mobileCode(String phone, String mobileCode) {MobilecodeAuthenticationToken mobilecodeAuthenticationToken = new MobilecodeAuthenticationToken(phone, mobileCode);Authentication authenticate = null;try {authenticate = authenticationManager.authenticate(mobilecodeAuthenticationToken);} catch (Exception e) {e.printStackTrace();return Result.error("验证码错误");}String token = UUID.randomUUID().toString().replace("-", "");return Result.ok(token);}
}

 3、用token 校验过滤

 认证成功后通常会返回一个token令牌(如jwt等),后续我们将token放到请求头中进行请求,后端校验该token,校验成功后再访问相应的接口

@Component
@WebFilter
public class TokenAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {String token = httpServletRequest.getHeader("token");// 如果没有token,跳过该过滤器if (!StringUtils.isEmpty(token)) {// 模拟redis中的数据Map map = new HashMap();map.put("test_token1", new MyUser("admin", new BCryptPasswordEncoder().encode("123456"), new ArrayList<>(Arrays.asList("p1", "p2"))));map.put("test_token2", new MyUser("root", new BCryptPasswordEncoder().encode("123456"), new ArrayList<>(Arrays.asList("p1", "p2"))));// 这里模拟从redis获取token对应的用户信息MyUser myUser = (MyUser)map.get(token);if (myUser != null) {UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(myUser, null, myUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authRequest);} else {throw new PreAuthenticatedCredentialsNotFoundException("token不存在");}}filterChain.doFilter(httpServletRequest, httpServletResponse);}
}

 修改配置 MySecurityConfigurer

    @Overridepublic void configure(HttpSecurity http) throws Exception {http// 关闭csrf.csrf().disable()// 权限配置,登录相关的请求放行,其余需要认证.authorizeRequests().antMatchers("/login/*").permitAll().anyRequest().authenticated().and()// 添加token认证过滤器.addFilterAfter(tokenAuthenticationFilter, LogoutFilter.class)// 不使用session会话管理.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);}

二、认证

1、基于方法的授权

当我们想要开启spring方法级安全时,只需要在任何 @Configuration实例上使用@EnableGlobalMethodSecurity 注解就能达到此目的。同时这个注解为我们提供了prePostEnabled 、securedEnabled 和 jsr250Enabled 三种不同的机制来实现同一种功能。

    @GetMapping("/hello")@PreAuthorize("hasAuthority('cece2')")public String hello(){return "Hello Spring security";}

 也可以

@PreAuthorize("@el.verify('cece2')")  然后往容器中添加el 实例,然后使用自定义的方法验证权限

2、基于Request的授权

http.authorizeRequests(authorize -> authorize// 添加授权配置.requestMatchers("/user/list").hasAnyAuthority("USER_LIST").requestMatchers("/user/add").hasAnyAuthority("USER_ADD")// 对所有请求开启授权保护.anyRequest()// 已认证的请求会被自动授权.authenticated())

三、相关配置

Security 常用配置-CSDN博客

四:参考

Spring Security入门教程

​​​​​​Spring Security 实现多种认证方式

http://www.lryc.cn/news/436871.html

相关文章:

  • Lua5.3 参考手册
  • Centos如何配置阿里云的yum仓库作为yum源?
  • 力扣139-单词拆分(Java详细题解)
  • CSS —— display属性
  • BTC ETF资金流入暴涨400%,市场下一步将如何发展?
  • 视频监控管理平台LntonAIServer视频智能分析抖动检测算法应用场景
  • 初识php库管理工具composer的体验【爽】使用phpword模板功能替换里面的字符串文本
  • 每日一问:C++ 如何实现继承、封装和多态
  • STM32常用数据采集滤波算法
  • 二分系列(二分查找)9/12
  • 如何通过可视化大屏,助力智慧城市的“城市微脑”建设?
  • 何时空仓库
  • 美创获评CNVD年度原创漏洞发现贡献单位!
  • Spring 循环依赖原理及解决方案
  • 【数据结构与算法 | 灵神题单 | 插入链表篇】力扣2807, LCR 029, 147
  • 瑞芯微rv1126 Linux 系统,修改系统时区,包有效方法
  • 系统架构设计师:数据库设计
  • 代码随想录刷题day31丨56. 合并区间,738.单调递增的数字,总结
  • 深圳建站公司-如何做网站
  • Google Earth Engine(GEE)——随时间推移的降雨趋势案例分析(大规模气候监测)
  • 从新手到高手:用这9个策略让ChatGPT成为你的私人顾问!
  • 高精度定位系统中的关键技术:GGA、EHP、RTMC、IMU、GNSS、INS 和 RTK 的协同工作
  • Spring3~~~
  • 微服务CI/CD实践(五)Jenkins Docker 自动化构建部署Java微服务
  • 泰州高新区法院多层面强化固定资产管理
  • JDBC简介与应用:Java数据库连接的核心概念和技术
  • 倒反天罡!这个AI风格模型可自由训练,还能批量生成同风格图像
  • Stable Diffusion绘画 | ControlNet应用-Inpaint(局部重绘):更完美的重绘
  • 电网谐波越限怎么处理
  • Redis中的AOF重写过程及其实际应用