SpringSecurity实现表单提交操作,连接数据库,实现注册和登录Spring Security实现表单提交操作,连接数据库,实现注册和登录
Spring Security 是 Spring 家族中的⼀个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经发展了多年了,但是使⽤的并不多,安全管理这个领域,⼀直是 Shiro 的天下。
相对于 Shiro,在 SSM/SSH 中整合 Spring Security 都是⽐较⿇烦的操作,所以,Spring Security 虽然功能⽐ Shiro 强⼤,但是使⽤反⽽没有 Shiro 多(Shiro 虽然功能没有 Spring Security 多,但是对于⼤部分项⽬⽽⾔,Shiro 也够⽤了)。
⾃从有了 Spring Boot 之后,Spring Boot 对于 Spring Security 提供了 ⾃动化配置⽅案,可以零配置使⽤ Spring Security。
因此,⼀般来说,常见的安全管理技术栈的组合是这样的:
SSM + Shiro
Spring Boot/Spring Cloud + Spring Security
注意,这只是⼀个推荐的组合⽽已,如果单纯从技术上来说,⽆论怎么组合,都是可以运⾏的。
我们来看下具体使⽤。
⼀、创建⼀个springboot项⽬,并引⼊相关依赖
<!--springsecurity需要引⼊的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--jpa依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
实现登录
⼀些基本的DAO层的类不写了。
继承WebSecurityConfigurerAdapter类
WebSecurityConfigurerAdapter 是SpringSecurity 提供的⽤于我们扩展⾃⼰的配置
实现WebSecurityConfigurerAdapter经常需要重写的:
1、configure(AuthenticationManagerBuilder auth);
2、configure(WebSecurity web);
3、configure(HttpSecurity http);
//WebSecurityConfigurerAdapter 是SpringSecurity 提供的⽤于我们扩展⾃⼰的配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
//该⽅法的主要⽤法
//1.通过Java的⽅式配置⽤户名/密码
//2.在这⾥完成获得数据库中的⽤户信息
//3.密码⼀定要加密(加密的⽅式⼀定要和注册是加密的⽅式⼀致)
//4.登录认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//下⾯这两⾏配置表⽰在内存中配置了两个⽤户
/
/ auth.inMemoryAuthentication()
// .withUser("admin").roles("admin").password("a")
// .withUser("admin").roles("admin").password("a")
// .and()
// .withUser("user").roles("user").password("a");
//Spring Security 提供了BCryptPasswordEncoder类,实现Spring的PasswordEncoder接⼝使⽤BCrypt强哈希⽅法来加密密码。
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
//可以⾃定义加密⽅式,实现PasswordEncoder接⼝,
// springsercurity后⾯的版本必须指定PasswordEncoder实现类,但如果不想加密的话,也可以通过空实现的⽅式
// @Bean
// PasswordEncoder passwordEncoder() {
// return new JWTPasswordEncoder();
// }
@Override
protected void configure(HttpSecurity http) throws Exception {
//http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
http
.authorizeRequests()//开启登录配置
// 如果有允许匿名的url,填在下⾯,这些url可以不经过过滤
.antMatchers("/login.html","/register.html","/register").permitAll()
/
/注意该处要与数据库的ROLE_后⾯部分保持⼀致,⼤⼩写也要⼀致
.antMatchers("/hello").hasRole("ADMIN")//表⽰访问 /hello 这个接⼝,需要具备 ADMIN 这个⾓⾊
.anyRequest().authenticated()//表⽰剩余的其他接⼝,任何⽤户登录之后就能访问
.and()
//开启表单登录,该⽅法会应⽤ FormLoginConfigurer 到HttpSecurity上,后续会被转换为对应的Filter
.formLogin()
//定义登录页⾯,未登录时,访问⼀个需要登录之后才能访问的接⼝,会⾃动跳转到该页⾯
.loginPage("/login.html")//默认跳转的是springsecurity⾃带的登录界⾯
//默认是 /login,但是当配置了.loginPage("/login.html"),默认值就变成了/login.html
.loginProcessingUrl("/doLogin")
// 设置登陆成功页
.
defaultSuccessUrl("/index.html")
//定义登录时,⽤户名的 key,默认为 username
.usernameParameter("uname")
// 定义登录时,⽤户密码的 key,默认为 password
.passwordParameter("password")
// 登录成功的处理器
// .successHandler(new AuthenticationSuccessHandler() {
// @Override
// public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
// resp.setContentType("application/json;charset=utf-8");
// resp.sendRedirect("index.html");
// PrintWriter out = Writer();
// out.write("success");
// out.flush();
//
// }
// })
//登录失败的处理器
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOExceptio n, ServletException, IOException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = Writer();
out.write("fail");
out.flush();
}
}
})
//和表单登录相关的接⼝统统都直接通过
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.
logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletE xception {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = Writer();
out.write("logout success");
out.flush();
}
})
.permitAll()
.and()
.httpBasic()
.and()
.csrf().disable();// 关闭CSRF跨域
}
//直接过滤掉该地址,即该地址不⾛ Spring Security 过滤器链
@Override
public void configure(WebSecurity web) throws Exception {
// 设置拦截忽略⽂件夹,可以对静态资源放⾏
web.ignoring().antMatchers("/css/**", "/js/**","/register.html");
}
}
需要注意的是loginPage和loginProcessingUrl,默认值都是 /login。如果只配置loginPage⽽不配置loginProcessingUrl的话。那么loginProcessingUrl默认就是loginPage的值
如果只配置loginProcessUrl,就会⽤不了⾃定义登陆页⾯,security会使⽤⾃带的默认登陆页⾯。换句话说,如果跳转页⾯
是/login,提交表单/toLogin,那最好就是loginPage配/login,loginProcessingUrl配/toLogin
loginPage就是登录页⾯的url地址loginProcessingUrl是验证的登录url地址 如果不设则默认和loginPage⼀样。
实现UserDetailsService接⼝,这⼀步是⾮常重要的!!
整个Spring Security连接数据库的核⼼就是实现UserDetaisService接⼝
重写接⼝loadUserByUsername这个⽅法,在其中完成数据库的查询⼯作,并将得到的admin返回就可以了
实现UserDetailsService接⼝
整个Spring Security连接数据库的核⼼就是实现UserDetaisService接⼝
重写接⼝loadUserByUsername这个⽅法,在其中完成数据库的查询⼯作,并将得到的⾓⾊权限返回。
@Service
//重写UserDetailsService的loadUserByUsername⽅法
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserDao mapper;
PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User user =mapper.findByusername(name);
if (user==null){
return null;
}else {
web端登录//创建⼀个权限的集合
Collection<GrantedAuthority> authorities = new ArrayList<>();
//添加获取权限
authorities.add(new Role()));
//把对象信息(⽤户名,密码,权限)存⼊对象,返回该对象,controller层直接调⽤
//如果数据库未加密需要添加以下注释的两⾏代码,但⼀般来说数据库是不能⽤明⽂来存密码的,太不安全了
// org.userdetails.User user2 =new org.userdetails.Username(), Pwd()), authorities);
org.userdetails.User user2 =new org.userdetails.Username(), P wd(), authorities);
// System.out.println("管理员信息:"+Username()+" "+Pwd())+" "+Authorities());
return user2;
}
}
}
可以看到上⾯有两个User类,因为当前建⽴实体类的时候,没有考虑到springsecurity⾃带了⼀个类,也叫User,专门⽤来⽤户名,密码和权限的,后⾯也就不想改了。
<h1>登录界⾯</h1>
<form action="doLogin" method="post">
<!--要与配置类的属性名⼀致-->
⽤户名:<input type="text" name="uname">
密码:<input type="text" name="password">
<input type="submit" value="登录">
</form>
注册加密,存⼊数据库
UserController类
@GetMapping("/register")
public String register(String uname,String password){
User user=new User();
user.setUsername(uname);
user.setPwd(password);
user.setRole("ROLE_USER");
if(userBiz.insert(user)==true){
return "login.html";
}else{
return "register.html";
}
}
UserBiz类
public boolean insert(User user){
encryptPassword(user);
if(userDao.save(user)!=null)
return true;
else
return false;
};
private void encryptPassword(User user){
String password = Pwd();
password = new BCryptPasswordEncoder().encode(password);
System.out.println(password);
user.setPwd(password);
}
<h1>注册界⾯</h1>
<!-- 需要在springsecrurity处,排除掉springsecrurity对register的请求过滤-->
<form action="/register" method="post">
<!--要与配置类的属性名⼀致-->
⽤户名:<input type="text" name="uname">
密码:<input type="text" name="password">
<input type="submit" value="注册">
</form>
关于加密
任何应⽤考虑到安全,绝不能明⽂的⽅式保存密码到数据库。密码应该通过哈希算法进⾏加密。
有很多标准的算法⽐如SHA或者MD5,结合salt(盐)是⼀个不错的选择。
Spring Security提供BCryptPasswordEncoder类,实现Spring的PasswordEncoder接⼝使⽤BCrypt强哈希⽅法来加密密码。BCrypt强哈希⽅法 每次加密的结果都不⼀样
既然每次加密结果不⼀样,就不可以通过相同字符串加密后的结果来判定是不是同⼀个字符串了,这就更加增强了安全性。
如果需要判断是否是原来的密码,需要⽤它⾃带的⽅法。
加密:
BCryptPasswordEncoder encode = new BCryptPasswordEncoder();
判断:
需要通过⾃带的⽅法 matches 将未经过加密的密码和已经过加密的密码传进去进⾏判断,返回布尔值。
encode.matches(Password());
数据库端的前缀
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论