赞
踩
作用:做登陆【认证】和授权的
代码位置:E:\010-SpringSecurity\002-springsecurity-thymeleaf
进入移动互联网时代,大家每天都在刷手机,常用的软件有微信、支付宝、头条等,下边拿微信来举例子说明认证相关的基本概念,在初次使用微信前需要注册成为微信用户,然后输入账号和密码即可登录微信,输入账号和密码登录微信的过程就是****认证****。
系统为什么要认证?
http://127.0.0.1:8080/getAllUser
http://127.0.0.1:8080/addUser
http://127.0.0.1:8080/updateUser
http://127.0.0.1:8080/deleteUser
认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
认证 :*用户认证就是判断一个用户的身份是否合法的过程*,用户去访问系统资源(url接口)时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。
官网: https://spring.io/projects/spring-security
中文文档: https://www.springcloud.cc/spring-security.html
Spring Security是一个能够为基于Spring的企业应用系统提供****声明式*****(注解)的安全访问控制解决方案的安全框架*。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
以上解释来源于百度百科。可以一句话来概括,SpringSecurity 是一个安全框架。
创建springboot 项目 添加依赖 springsecurity
启动类
@SpringBootApplication
@EnableWebSecurity // 启动security 5.0后 默认开启,可以不写
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
public class HelloController {
@GetMapping("hello")
public String hello() {
return "hello security";
}
}
http://127.0.0.1:8080/hello
发现我们无法访问hello这个请求,这是因为spring Security默认拦截了所有请求
使用user+启动日志里面的密码登陆
登录后就可以访问我们的hello了
访问: http://localhost:8080/logout
也可以在配置文件配置登录账号和密码
spring:
security:
user:
name: admin #默认使用的用户名
password: 123456 #默认使用的密码
我们在HelloController中创建
/*两种获取 用的认证后的信息*/ @RequestMapping("/getUserInfo") public Principal getUserInfo(Principal principal){ return principal; } /*推荐使用 */ @RequestMapping("/getUserInfo2") public Object getUserInfo2(){ /*从安全上下文中获取 用户 认证对象(用户信息)*/ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Object principal = authentication.getPrincipal(); return principal; // Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // return authentication;
获取到一串json字符串
重写两个方法 configure(AuthenticationManagerBuilder auth) 和configure(HttpSecurity http)
package com.powernode.config; @Configuration //创建认证配置类 继承 WebSecurityConfigurerAdapter public class SecurityConfig extends WebSecurityConfigurerAdapter { //重写 configure 方法 /*配置多用户*/ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // super.configure(auth); /*配置在内存中的 账号密码 不配置可能会配置不成功*/ auth.inMemoryAuthentication() .withUser("admin")//账号 .password(passwordEncoder().encode("123456"))//密码,使用加密器转码 .roles("ADMIN")//角色,权限 .authorities("sys:add","sys:delete","sys:update","sys:query","sys:export")//权限,(好比 给人钥匙) .and() .withUser("test") .password(passwordEncoder().encode("123456")) .roles("TEST") .authorities("sys:add","sys:query") .and() .withUser("powernode") .password(passwordEncoder().encode("123456")) .roles("VIP") ; } /*http请求配置 给请求加权限, 没有加权限的都可以访问 无权限默认访问error目录下的403.html*/ @Override protected void configure(HttpSecurity http) throws Exception { // super.configure(http);// 不使用父类的 方法, 需要 提供登录配置 http.formLogin().successForwardUrl("/welcome").failureForwardUrl("/fail"); /*设置 资源所需要的 权限 (好比 门上锁)*/ http.authorizeRequests() //匹配资源与权限 资源 权限(角色) .antMatchers("/add").hasAnyAuthority("sys:add") .antMatchers("/delete").hasAnyAuthority("sys:delete") .antMatchers("/update").hasAnyAuthority("sys:update") .antMatchers("/select").hasAnyAuthority("sys:query") .antMatchers("/export").hasAnyAuthority("sys:export") //给 父路径下 其所有资源 配置 权限 (觉得) .antMatchers("/vip/**").hasRole("VIP") ; } /*强制要求配置 密码加密器*/ @Bean// 将对象 交给 spring容器 管理 public PasswordEncoder passwordEncoder() { // return NoOpPasswordEncoder.getInstance();//不加密 return new BCryptPasswordEncoder(); } /*测试加密器*/ public static void main(String[] args) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); /*加密*/ String encode1 = passwordEncoder.encode("123456"); String encode2 = passwordEncoder.encode("123456"); String encode3 = passwordEncoder.encode("123456"); /*每次 加密的结果不一样*/ System.out.println(encode1); System.out.println(encode2); System.out.println(encode3); /*解密:匹配 密码是否一致 不能 使用 equals方法*/ System.out.println(passwordEncoder.matches("123456", encode1)); System.out.println(passwordEncoder.matches("123456", encode2)); System.out.println(passwordEncoder.matches("123456", encode3)); } }
只需要在controller对应的方法上添加注解即可了,不需要再webSecurityConfig中配置匹配的url和权限了,这样就爽多了
*@PreAuthorize 在方法调用前进行权限检查*
@PostAuthorize 在方法调用后进行权限检查
上面的两个注解如果要使用的话必须加上
*@EnableGlobalMethodSecurity(prePostEnabled = true)*
controller 层
/* 没有权限处理器 * */ @Component public class AppAccessDeniedHandler implements AccessDeniedHandler { @Autowired private ObjectMapper objectMapper; @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { System.out.println("没有权限"); /*设置响应编码 放置乱码*/ response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); /*创建统一返回类型对象*/ Result result = new Result(); result.setCode(403); result.setMsg("没有权限"); /*将 result 转换为 json数据*/ String json = objectMapper.writeValueAsString(result); /*响应json数据*/ PrintWriter writer = response.getWriter(); writer.write(json); writer.flush(); } }
/*登录失败处理器*/ @Component public class AppAuthenticationFailureHandler implements AuthenticationFailureHandler { @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { System.out.println("登录失败"); /*设置响应编码 放置乱码*/ response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); /*创建统一返回类型对象*/ Result result = new Result(); result.setCode(-1); result.setMsg("登录失败"); if (e instanceof BadCredentialsException) { result.setData("失败原因:密码不正确"); } else if (e instanceof DisabledException) { result.setData("失败原因:账号被禁用"); } else if (e instanceof UsernameNotFoundException) { result.setData("失败原因:查无此账号"); } else if (e instanceof CredentialsExpiredException) { result.setData("失败原因:密码过期"); } else if (e instanceof AccountExpiredException) { result.setData("失败原因:账号过期"); } else if (e instanceof LockedException) { result.setData("失败原因:账号被锁定"); } /*将 result 转换为 json数据*/ String json = objectMapper.writeValueAsString(result); /*响应json数据*/ PrintWriter writer = response.getWriter(); writer.write(json); writer.flush(); } }
/* * 自定义的 登录成功处理器 直接返回json数据 * */ @Component public class AppAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; /** * @param request 请求对象 * @param response 响应对象 * @param authentication 认证对象(用户信息) * @throws IOException * @throws ServletException */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("登录成功"); /*设置响应编码 放置乱码*/ response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); /*创建统一返回类型对象*/ Result result = new Result(); result.setCode(200); result.setMsg("登录成功"); result.setData(authentication);//只是为了显示 用户信息 实际情况不会返回 /*将 result 转换为 json数据*/ String json = objectMapper.writeValueAsString(result); /*响应json数据*/ PrintWriter writer = response.getWriter(); writer.write(json); writer.flush(); } }
/*登出成功处理器*/ @Component public class AppLogoutSuccessHandler implements LogoutSuccessHandler { @Autowired private ObjectMapper objectMapper; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("登出成功"); /*设置响应编码 放置乱码*/ response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); /*创建统一返回类型对象*/ Result result = new Result(); result.setCode(200); result.setMsg("登出成功"); result.setData(authentication);//只是为了显示 用户信息 实际情况不会返回 /*将 result 转换为 json数据*/ String json = objectMapper.writeValueAsString(result); /*响应json数据*/ PrintWriter writer = response.getWriter(); writer.write(json); writer.flush(); } }
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { /*注入 登录成功处理器*/ @Autowired private AppAuthenticationSuccessHandler appAuthenticationSuccessHandler; /*注入 登录 失败处理器*/ @Autowired private AppAuthenticationFailureHandler appAuthenticationFailureHandler; /*注入 没有权限处理器*/ @Autowired private AppAccessDeniedHandler appAccessDeniedHandler; /*注入 登出成功处理器*/ @Autowired private AppLogoutSuccessHandler appLogoutSuccessHandler; @Autowired private AppUserDetailsService appUserDetailsService; //验证码拦截器注入 @Autowired private ValidateCodeFilter validateCodeFilter; /*配置多用户*/ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(appUserDetailsService); } /*http请求配置*/ @Override protected void configure(HttpSecurity http) throws Exception { // super.configure(http);// 不使用父类的 方法, 需要 提供登录配置 /*没权权限的处理*/ // http.exceptionHandling().accessDeniedHandler(appAccessDeniedHandler); /*登录*/ // 配置登录之前添加一个验证码的过滤器 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class); http.formLogin() .usernameParameter("uname")//页面表单账号的参数名 默认 为 username .passwordParameter("pwd")//页面表单密码的参数名 默认 为 password .loginPage("/index/toLogin")//定义登录页面的 请求 地址(转发到登录页面) .loginProcessingUrl("/login/doLogin")// 表单提交的 地址(不需要提供),登录验证..... .successForwardUrl("/index/toIndex")//登录成功 跳转的路径 .failureForwardUrl("/index/toLogin")//登录失败 跳转的路径 // .successHandler(appAuthenticationSuccessHandler)//登录成功处理器 // .failureHandler(appAuthenticationFailureHandler)//登录失败处理器 .permitAll(); ; /*登出*/ http.logout() .logoutUrl("/logout")//登出的 请求地址 .logoutSuccessUrl("/index/toLogin")//登出成功后 访问的路径 // .logoutSuccessHandler(appLogoutSuccessHandler)//登出成功处理器 .permitAll() ; /*设置 资源所需要的 权限 (好比 门上锁)*/ http.authorizeRequests() .antMatchers("/code/img")// 放行验证码的路径 // .mvcMatchers("/index/toLogin", "/index.html").permitAll()//不需要认证就可以访问 .permitAll() .anyRequest().authenticated()//所有请求都需要登录认证 才能进行 ; /*禁用csrf跨域请求攻击 如果不禁用 自定义的登录页面无法登录*/ http.csrf().disable(); } /*资源服务匹配放行:静态资源*/ @Override public void configure(WebSecurity web) throws Exception { // super.configure(web); web.ignoring().antMatchers("/css/**"); } /*强制要求配置 密码加密器*/ @Bean// 将对象 交给 spring容器 管理 public PasswordEncoder passwordEncoder() { // return NoOpPasswordEncoder.getInstance();//不加密 return new BCryptPasswordEncoder(); } }
/* * 自定义认证 * * */ @Component public class AppUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Autowired private SysUserDao sysUserDao; /** * @param username 用户输入的 账号 * @return 用户信息(账号密码 权限,是否过期等 方法) * @throws UsernameNotFoundException 没有账号会抛该异常类型 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { /*通过账号查询用户*/ SysUser sysUser = sysUserDao.queryUserByUsername(username); if (sysUser == null) { throw new UsernameNotFoundException("用户名不存在"); } /*根据用户的id 获取用户的 权限字符串 集合*/ List<String> permissions = sysUserDao.queryPermissionByUserId(sysUser.getUserId()); /*将 字符串集合 转换为 权限对象集合*/ List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (String permission : permissions) { /*根据权限字符串 生成 对应的 权限对象*/ authorities.add(new SimpleGrantedAuthority(permission)); } /*将权限集合封装到 user对象中*/ sysUser.setPermissions(authorities); return sysUser; } }
/** * (SysUser)实体类 * 实现序列化接口和 UserDetails接口 * * @author makejava * @since 2022-10-25 15:32:59 */ public class SysUser implements Serializable, UserDetails { private static final long serialVersionUID = -54448691274655158L; /** * 编号 */ private Integer userId; /** * 登陆名 */ private String username; /** * 密码 */ private String password; /** * 性别 */ private String sex; /** * 地址 */ private String address; /** * 是否启动账户0禁用 1启用 */ private Integer enabled; /** * 账户是否没有过期0已过期 1 正常 */ private Integer accountNoExpired; /** * 密码是否没有过期0已过期 1 正常 */ private Integer credentialsNoExpired; /** * 账户是否没有锁定0已锁定 1 正常 */ private Integer accountNoLocked; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Integer getEnabled() { return enabled; } public void setEnabled(Integer enabled) { this.enabled = enabled; } public Integer getAccountNoExpired() { return accountNoExpired; } public void setAccountNoExpired(Integer accountNoExpired) { this.accountNoExpired = accountNoExpired; } public Integer getCredentialsNoExpired() { return credentialsNoExpired; } public void setCredentialsNoExpired(Integer credentialsNoExpired) { this.credentialsNoExpired = credentialsNoExpired; } public Integer getAccountNoLocked() { return accountNoLocked; } public void setAccountNoLocked(Integer accountNoLocked) { this.accountNoLocked = accountNoLocked; } @Override//是1返回true 不是就返回false public boolean isAccountNonExpired() { return accountNoExpired.equals(1); } @Override public boolean isAccountNonLocked() { return accountNoLocked.equals(1); } @Override public boolean isCredentialsNonExpired() { return credentialsNoExpired.equals(1); } @Override public boolean isEnabled() { return enabled.equals(1); } /*权限集合*/ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return permissions; } /*封装权限集合*/ private List<SimpleGrantedAuthority> permissions = new ArrayList<>(); public List<SimpleGrantedAuthority> getPermissions() { return permissions; } public void setPermissions(List<SimpleGrantedAuthority> permissions) { this.permissions = permissions; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。