实验一、引入spring security实验二、从mysql中加载用户
前置知识①、编写对象实现UserDetail接口②、实现UserDetailsService,这里使用了mysql+mybatis③、数据库配置和创建表:③、配置Spring Security Config④、编写controller⑤、登录加密
实验一、引入spring security①、pom文件添加依赖
org.springframework.boot spring-boot-starter-securityorg.springframework.security spring-security-testtest
②、编写controller测试
@Controller public class UserContorller { @GetMapping("/hello") @ResponseBody public String hello(){ return "hello hello" ; } }
③、启动应用测试:localhost:8080/hello 发现需要经过spring security的登录认证路径 /login,其中用户名默认为user,密码是随机生成的uuid,打印在控制台中。
源码分析:
在UsernamePasswordAuthenticationFilter#attemptAuthentication方法中,已经定义了默认的username和password字段,用户提交上来的表单信息将在http
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; //创建路径匹配器 private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST"); private String usernameParameter = "username"; private String passwordParameter = "password"; private boolean postonly = true; public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { else { // 从request中获取用户名和密码 String username = this.obtainUsername(request); username = username != null ? username : ""; username = username.trim(); String password = this.obtainPassword(request); password = password != null ? password : ""; //生成一个用户名和密码身份验证的令牌 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); //设置身份请求的认证信息 this.setDetails(request, authRequest); //返回一个经过身份验证的对象 return this.getAuthenticationManager().authenticate(authRequest); } } // 从Request中获取参数 protected String obtainPassword(HttpServletRequest request) { return request.getParameter(this.passwordParameter); }实验二、从mysql中加载用户 前置知识
spring security 不关心要登录的用户名称和密码从哪里来,它们可以来自配置文件,内存或者是数据库。
Spring Security 提供了一个配置类让我们配置它的主要行为: WebSecurityConfigurerAdapter
这个类有三个重要的重载函数,通过重写它们我们可以做许多工作,如定义spring security的访问规则,拦截规则,认证来源等等
⭐AuthenticationManagerBuilder
用来配置全局的认证相关的信息,核心是AuthenticationProvider和UserDetailsService,分别提供不同的认证方式(如密码+用户名或密码+邮箱/手机等)和自定义用户的细节。
这个类中有几个方法可以实现不同用户的存储,如InMemoryUserDetailsManagerConfigurer是基于内存的,JdbcUserDetailsManagerConfigurer是基于Jdbc方式的
而userDetailsService方法就是本实验的重点,允许我们自定义用户的实现。
它接收一个userDetailsService类的参数,返回一个DaoAuthenticationConfigurer
//AuthenticationManagerBuilder.java部分源码 public InMemoryUserDetailsManagerConfigurer inMemoryAuthentication() throws Exception { return (InMemoryUserDetailsManagerConfigurer)this.apply(new InMemoryUserDetailsManagerConfigurer()); } public JdbcUserDetailsManagerConfigurer jdbcAuthentication() throws Exception { return (JdbcUserDetailsManagerConfigurer)this.apply(new JdbcUserDetailsManagerConfigurer()); } publicDaoAuthenticationConfigurer userDetailsService(T userDetailsService) throws Exception { this.defaultUserDetailsService = userDetailsService; return (DaoAuthenticationConfigurer)this.apply(new DaoAuthenticationConfigurer(userDetailsService)); }
⭐HttpSecurity
用于权限规则的配置,提供了许多相关的方法,通过这些方法的组合,可以自定义登录,登出的url地址,页面,url拦截规则等等。
它被设计成链式调用,在执行每个方法后,都会返回一个预期的上下文,便于连续调用,不需要关心每个方法到底返回了什么
authorizeRequests() //返回一个url拦截注册器, //可以调用它提供的anyRequest(),antMachers(),和regexMatchers()等方法匹配系统的url formLogin() httpBasic() //这两个方法都声明了spring securtiy提供的表单认证方法,分别返回对应的配置器 csrf() //spring security提供的跨站请求伪造防护功能,默认开启 logout() //登出相关 and()
configure (HttpSecurity)的默认方法如下:
protected void configure(HttpSecurity http) throws Exception { this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http.authorizeRequests((requests) -> { ((AuthorizedUrl)requests.anyRequest()).authenticated(); }); http.formLogin(); http.httpBasic(); }
可以看到它已经有一些默认的特性:
所有请求都需要认证允许表单登录进行身份验证允许用户使用http基本认证
知识点:
http基本认证是RFC2616中定义的一种认证模式,有4个步骤:客户端发起一条没有携带认证信息的请求服务器返回一条401Unauthorized响应,并在WWW-Authentication首部认证说明认证形式,当进行http基本认证后,WWW-Authentication会被设置为Basic realm=“被保护页面”客户端收到401 Unauthorized响应后,d出对话框,询问用户名和密码。当用户完成后,客户端将用户名和密码使用冒号拼接并编码为base64形式,然后放入请求Authorization首部发送给服务器。服务端解码从客户端发来的用户名和密码,并验证正确后,返回客户端请求的报文。
⭐WebSecurity
配置全局请求忽略规则,一般用来放行静态资源,如html,css,js等
回到实验,我们想要从Mysql数据库中查询出用户的信息给spring security校验,关键在于把得到的数据包装成它认识的样子,于是它提供了 UserDetailsService 和 UserDetails ,我们直接看源码:
UserDetail是个接口,定义了用户的信息
public interface UserDetails extends Serializable { Collection extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
UserDetailsService 也是个接口,而且只有一个方法,就是根据参数username来查找用户,并返回一个UserDetails 对象
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
实现UserDetails接口定义需要认证的用户,实现UserDetailsService定义如何从username获得用户对象。
①、编写对象实现UserDetail接口@Data public class User implements UserDetails { Long id ; String userName ; String passWord ; boolean enabled ; List②、实现UserDetailsService,这里使用了mysql+mybatisauthorities; @Override public Collection extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return passWord; } @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 enabled; } }
@Service public class UserService implements UserDetailsService { @Autowired UserMapper userMapper ; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //从数据库中查,下文有配置 User user = userMapper.getUserByName(username); if(user==null) { throw new RuntimeException("用户不存在!"); } return user ; } }③、数据库配置和创建表:
- pom文件添加依赖
org.mybatis.spring.boot mybatis-spring-boot-starter2.2.1 com.baomidou mybatis-plus-boot-starter3.5.0 org.springframework.boot spring-boot-devtoolsruntime true com.alibaba druid-spring-boot-starter1.1.17 mysql mysql-connector-javaruntime
- 配置数据库: 在application.properties文件中完成数据库的配置,注意替换成你自己的数据库信息
spring.datasource.type= com.alibaba.druid.pool.DruidDataSource spring.datasource.url= jdbc:mysql:///${你的数据库名称}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai spring.datasource.username= root spring.datasource.password=${你的数据库密码} # 指定sql映射文件的位置 mybatis.mapper-locations = classpath*:com/wingchi/mapper/*.xml
- 创建用户表,并插入数据
(博主的用户表是项目中在用的,有一些其他的字段不需要理会)
CREATE TABLE `user` ( `id` int(10) PRIMARY KEY AUTO_INCREMENT, `username` varchar(20) NOT NULL, `password` varchar(15) NOT NULL, `createTime` datetime, `lastLoginTime` datetime, `enabled` boolean NOT NULL )ENGINE=InnoDB CHARSET=utf8; insert into user values(1,'wingchi','234567',null,null,true) ; insert into user values(2,'hk','123456',null,null,true) ;
- 创建dao层,mybatis的知识点:
//UserMapper.java @Mapper public interface UserMapper { public User getUserByName(@Param("name") String name ) ; }
UserMapper.xml (注意要和java文件同名!)
③、配置Spring Security Configselect * from `user` where name = #{name}
回到实验的开头铺垫知识,我们需要重写WebSecurityConfigurerAdapter中的一些方法完成我们的配置
这里贴出我的代码:
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new UserService()); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() // 禁用csrf .formLogin() // 开启http表单认证 .and() .authorizeRequests() // 身份认证请求 .antMatchers("/hello/**").permitAll() //任何匹配 /hello/**的路径,允许所有人访问 .anyRequest().authenticated(); //其他的所有请求,都需要认证证。 } }
踩坑记录♀️: 我在配置的时候,因为不知道有http认证这个知识点,既没有配置 httpBasic()方式,也没有配置 formLogin()方式,这样是没办法正常运行的。 了解http的几种认证方式可以参考
这里
@Controller public class UserController { @RequestMapping("/user") @ResponseBody public String user(String name ) { return "hello"+name; } @RequestMapping("/hello") @ResponseBody public String hello() { return "hello"; } }⑤、登录加密
从spring security5开始,就要求必须手动配置密码加密方式,spring官方推荐使用BCrypt加密,并明确指出sha和md5都是不安全的。所以这里使用BCrypt方式
配置十分简单:
@Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
spring security会自动应用这个bean。
参考:
https://blog.csdn.net/itguangit/article/details/78928886
https://blog.csdn.net/wangb_java/article/details/86676166
《spring security实战–陈木鑫》
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)