热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

渐进式学习springbootsecurity

1.装箱即用的springsecurity加入依赖,加个配置即可——应用场景:eurekaserver,hystrixdashboard,spring

1.装箱即用的spring security

加入依赖,加个配置即可——应用场景:eureka server,hystrix dashboard,springboot admin等,用于增加登录验证(基于COOKIE:jssessionId实现用户session的,不能用postman等工具来测试)。该应用场景效果基本nginx配置账号密码验证差不多

org.springframework.bootspring-boot-starter-security

spring:security:user:name: jwolfpassword: 123456

 

2.追加几个内存用户并配置其访问权限


import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;@EnableWebSecurity
public class MemoryUserSecurityConfig extends WebSecurityConfigurerAdapter {//用户认证@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//内存里面初始化几个用户auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())//添加用户,密码,角色.withUser("zs").password("123456").roles("AAA")//链式编程.and().withUser("ls").password("123456").roles("BBB").and().withUser("ww").password("123456").roles("CCC", "primary").authorities("PPP");//PPP权限}//用户授权 ant风格的path@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").permitAll() //应用首页所以用户都可以访问,完全无限制.antMatchers("/test-user/addUs*").hasRole("BBB") // 允许 AAA 角色访问.antMatchers("/test-user/deleteUser/**").hasAnyAuthority("PPP") //允许有"PPP"权限的访问.antMatchers("/test-user/updateUser").hasAnyRole("AAA", "BBB", "CCC").antMatchers("/test-user/findAllUsers").permitAll().anyRequest().authenticated() //其它path都有登录才能访问.and().formLogin();//指定支持基于表单的身份验证,会暴露出/login /logout等端点,默认登录成功调向登录前的,可以自定义登录页面登录成功和失败重定向的url,例如.formLogin().loginPage("/myLogin").successForwardUrl("/xxxx").failureForwardUrl("/yyy")}private class MyPasswordEncoder implements org.springframework.security.crypto.password.PasswordEncoder {@Overridepublic String encode(CharSequence charSequence) {return charSequence.toString(); //用户输入密码处理}@Overridepublic boolean matches(CharSequence charSequence, String s) {return s.equals(charSequence.toString());//与security上下文存储的用户密码比对}}
}

@RestController
@RequestMapping("/test-user")
public class TestUserController {@RequestMapping("/addUser")String addUser() {return "这是添加用户!!!";}@RequestMapping("/deleteUser")String deleteUser() {return "这是删除用户!!!";}@RequestMapping("/updateUser")String updateUser() {return "这是修改用户!!!";}@RequestMapping("/findAllUsers")String findAllUsers() {return "这是查询用户!!!";}
}

3.从DB获取用户信息进行权限控制

 配置类主要注入bean UserDetailsService

@Configuration
public class DBUserSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;//这里从数据库中读取数据到spring security上下文@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());}//用户授权 ant风格的path@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").permitAll() //应用首页所以用户都可以访问,完全无限制.antMatchers("/test-user/addUs*").hasRole("BBB") // 允许 AAA 角色访问.antMatchers("/test-user/deleteUser/**").hasAnyAuthority("PPP") //允许有"PPP"权限的访问.antMatchers("/test-user/updateUser").hasAnyRole("AAA", "BBB", "CCC").antMatchers("/test-user/findAllUsers").permitAll().anyRequest().authenticated() //其它path都有登录才能访问.and().formLogin();//指定支持基于表单的身份验证,会暴露出/login /logout等端点}@Beanpublic PasswordEncoder getPasswordEncoder(){return new PasswordEncoder() {@Overridepublic String encode(CharSequence password) {return password.toString();}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {String encodeStr = encode(rawPassword);return encodedPassword.equalsIgnoreCase(encodeStr);}};}
}

需要实现spring security的 UserDetailsService 接口,下面基本就是常规的业务类型代码了,这里注入的UserMapper

@Service
public class SecurityUserServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SecurityUser user = userMapper.selectByUsername(username);if (user == null) {throw new BadCredentialsException("用户名或密码错误");}return user;}
}

entity是这样的,需要实现spring secury的UserDetails

import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
@Getter
@Setter
public class SecurityUser implements UserDetails {private static final long serialVersionUID=1L;private long id;private String password;private String username;private List anthorities;@Overridepublic Collection getAuthorities() {return this.anthorities;}//账户是否未过期@Overridepublic boolean isAccountNonExpired() {return true;}//账户是否未锁定@Overridepublic boolean isAccountNonLocked() {return true;}//帐户密码是否未过期,一般有的密码要求性高的系统会使用到,比较每隔一段时间就要求用户重置密码@Overridepublic boolean isCredentialsNonExpired() {return true;}//账户是否可用@Overridepublic boolean isEnabled() {return true;}
}

userMapper.xml是这样的


4.关于role 与authority区别及权限注解的开启

使用权限注解需要启动类开启,该注解有几个参数,默认都是关的@EnableGlobalMethodSecurity(prePostEnabled = true),@DenyAll @RolesAllowed({"USER", "ADMIN"}) @PermitAll 等开关需要通过jsr250Enabled = true来开启@EnableGlobalMethodSecurity(prePostEnabled = true,jsr250Enabled = true)

核心配置类DBUserSecurityConfig的.antMatchers("/test-user/deleteUser/**").hasAnyAuthority("PPP") 改为权限配置应该是接口使用注解@PreAuthorize("hasAnyAuthority('PPP')"),如果数据存的rolename为ROLE_XXX,或存入SimpleGrantedAuthority的rolename有ROLE_前缀,就应该使用@PreAuthorize("hasAnyRole('PPP')"),role与authority区别参考https://www.cnblogs.com/Rocky_/p/11799772.html

5.整合JWT

增加jwt依赖

io.jsonwebtokenjjwt0.9.0

原生的security认证信息通过COOKIE-session保存会话session,用户客户端或服务端重启都会导致重新登录,多数情况需要整合jwt,这里较step3多了两个filter,一个用于登录成功将jwt token写到浏览器,一个用于访问资源时解析jwt token放入到security 认证上下文,免密加密用的security亲生的BCryptPasswordEncoder


import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.ibatis.javassist.bytecode.ByteArray;
import org.apache.tomcat.util.security.MD5Encoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import sun.security.provider.MD5;import java.util.ArrayList;
import java.util.Collection;@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").permitAll() //应用首页所以用户都可以访问,完全无限制.antMatchers("/test-user/addUs*").hasRole("BBB") // 允许 AAA 角色访问//.antMatchers("/test-user/deleteUser/**").hasAnyAuthority("PPP") //允许有"PPP"权限的访问.antMatchers("/test-user/updateUser").hasAnyRole("AAA", "BBB", "CCC").antMatchers("/test-user/findAllUsers").permitAll().anyRequest().authenticated() //其它path都有登录才能访问.and().csrf().disable().formLogin()//指定支持基于表单的身份验证,会暴露出/login /logout等端点.and().addFilter(new JWTLoginFilter(authenticationManager())).addFilter(new JWTAuthenticationFilter(authenticationManager()));}@Beanpublic PasswordEncoder getPasswordEncoder() {return new PasswordEncoder() {@Overridepublic String encode(CharSequence password) {//用户注册调用该方法进行密码加密// BCryptPasswordEncoder对相同字符串每次加密都结果都不一样,但matches()时都能比较成功,具有更高安全性return new BCryptPasswordEncoder().encode(password);}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {//用户登录将用户表单传入的rawPassword与数据库的密码比对。return new BCryptPasswordEncoder().matches(rawPassword,encodedPassword);}};}}

两个过滤器

import com.alibaba.fastjson.JSON;
import com.construn.vehicle.common.base.entity.ResultEntity;
import com.construn.vehicle.user.entity.SecurityUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.AuthenticationManager;
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.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.stream.Collectors;/*** 验证用户名密码正确后,生成一个token,并将token返回给客户端* 该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法 ,* attemptAuthentication:接收并解析用户凭证。* successfulAuthentication:用户成功登录后,这个方法会被调用,我们在这个方法里生成token并返回。*/
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;public JWTLoginFilter(AuthenticationManager authenticationManager) {this.authenticationManager = authenticationManager;}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {String username = request.getParameter("username");String password = request.getParameter("password");return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password, null));}@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) {Claims claims = Jwts.claims();SecurityUser user = (SecurityUser) auth.getPrincipal();claims.put("userId", user.getId());claims.put("role", auth.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList()));String token = Jwts.builder().setClaims(claims).setSubject(auth.getName()).setExpiration(new Date(System.currentTimeMillis() + 600 * 1000)).signWith(SignatureAlgorithm.HS512, "jwolf").compact();response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");ResultEntity result = ResultEntity.success(token, "登录成功");PrintWriter out;try {out = response.getWriter();out.print(JSON.toJSONString(result));out.flush();out.close();} catch (IOException e) {e.printStackTrace();}}
}

 

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;public class JWTAuthenticationFilter extends BasicAuthenticationFilter {public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}/*** 从request中获取token并解析,拿到用户信息,放置到SecurityContextHolder,这样便完成了springsecurity和jwt的整合。*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {String token = request.getHeader("Authorization");if (token == null || !token.startsWith("Bearer ")) {chain.doFilter(request, response);return;}Claims claims = Jwts.parser().setSigningKey("jwolf").parseClaimsJws(token.replace("Bearer ", "")).getBody();List roles = claims.get("role", List.class);List auth = roles.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());//如果controller还需要userId等信息其它更多信息,可以第一个参数传入甚至整个claims,最好不要再次从header里获取token二次解析出用户信息,//然后在controller直接SecurityContextHolder.getContext().getAuthentication()获取;UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken( claims.getSubject(), null, auth);SecurityContextHolder.getContext().setAuthentication(authentication);chain.doFilter(request, response);}}

其中登录过滤器可以优化,抽出JWT工具类,并可以不用该过滤器,使用登录成功处理器AuthenticationSuccessHandler进行替换,登录失败及登陆security也提供了类似的接口可以自己实现然后在security核心配置即可如登录成功: .successHandler(userLoginSuccessHandler)  参考https://www.tuicool.com/articles/Q7fU73E

 

 

 


推荐阅读
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • adfs是什么_培训与开发的概念
    adfs是什么_培训与开发的概念(如您转载本文,必须标明本文作者及出处。如有任何疑问请与我联系me@nap7.com)ADFS相关开发技术的中文资料相对匮乏,之前在弄这个东西的时候 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 单点登录原理及实现方案详解
    本文详细介绍了单点登录的原理及实现方案,其中包括共享Session的方式,以及基于Redis的Session共享方案。同时,还分享了作者在应用环境中所遇到的问题和经验,希望对读者有所帮助。 ... [详细]
author-avatar
杨barkema_252
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有