继续上一篇博客的项目,向下扩展
springsecurity中所有配置基本都来源于一个默认的WebSecurityConfigurerAdapter
所以我们要写一个类来继承它,放弃默认配置
叫SecurityConfig
添加一个login.html登录页面,登录成功以后跳到之前写的hello.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login page</title>
</head>
<body>
This is login page
<form action="/login" method="post">
username: <input name="username" /><br/>
password: <input name="password" /><br/>
<button type="submit">submit</button>
</form>
</body>
</html>
这个提交地址/login比较有意义,后面会用到配置
然后我们开始配置SecurityConfig
package com.security.securitydemo.security;
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.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @Author WQY
* @Date 2019/11/27 9:44
* @Version 1.0
*/
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
// 和login.html中表单提交的一直必须一样,这样才能让springsecurity帮你处理请求
.loginProcessingUrl("/login")
.and()
.authorizeRequests()
//添加不需要过滤的地址
.antMatchers("/index", "/login", "/error").permitAll()
.anyRequest()
.authenticated();
}
}
然后启动项目访问http://localhost:8880
就会出现自定义的登录界面
然后输入用户名密码后会出现403
不急别慌,因为开启了debug所以能在控制台看见走了一个CrsfFilter,这个filter需要一个参数,防止xss攻击的,但是我们不需要,所以我们禁掉。
修改SecurityConfig。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
// 和login.html中表单提交的一直必须一样,这样才能让springsecurity帮你处理请求
.loginProcessingUrl("/login")
.and()
.authorizeRequests()
.antMatchers("/index", "/login", "/error").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
在登陆就不会有403的错误了
但是会出现404的错误
因为没有设置登录成功的页面,所以http://localhost:8080作为了默认页面
然后我们在
SecurityConfig
中添加一个登录成功页面
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
// 和login.html中表单提交的一直必须一样,这样才能让springsecurity帮你处理请求
.loginProcessingUrl("/login")
.successForwardUrl("/hello")
.and()
.authorizeRequests()
.antMatchers("/index", "/login", "/error").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
可是这样我这会出现405
在修改SecurityConfig文件,添加默认成功页面
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")//处理登录请求接口
.successForwardUrl("/hello")
.defaultSuccessUrl("/hello",true)
.and()
.authorizeRequests()
.antMatchers("/index","/login","/error").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
这样输入账号密码后就进来了
自定义数据查询
经过上面的配置,我们实现了自定义登录页,但是数据是写死的
所以现在要从数据库获取用户信息
比如我们的数据库一共有两张表,user表和role表
package com.security.securitydemo.entity;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
* @Description
* @Author WQY
* @Date 2019-11-05
*/
@Data
@Entity
@Table ( name ="user" )
public class UserEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id" )
private Long id;
/**
* 用户名
*/
@Column(name = "username" )
private String username;
/**
* 密码
*/
@Column(name = "password" )
private String password;
/**
* 保存时间
*/
@Column(name = "savetime" )
private Date savetime;
/**
* 手机号
*/
@Column(name = "iphone" )
private String iphone;
/**
* 权限
*/
@Column(name = "authority" )
private Long authority;
/**
* 性别,1男,2女
*/
@Column(name = "sex" )
private String sex;
/**
* 组织机构编号
*/
@Column(name = "organization_num" )
private String organizationNum;
/**
* 组织机构名称
*/
@Column(name = "organization_name" )
private String organizationName;
}
package com.security.securitydemo.entity;
import lombok.Data;
import javax.persistence.*;
/**
* @Description
* @Author WQY
* @Date 2019-11-25
*/
@Data
@Entity
@Table ( name ="role" )
public class RoleEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id" )
private Long id;
@Column(name = "name" )
private String name;
}
并建立他们的Repository
package com.security.securitydemo.dao;
import com.security.securitydemo.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author WQY
* @date 2019/9/9 15:28
*/
public interface User_Jpa extends JpaRepository<UserEntity,Integer> {
UserEntity findByUsername(String username);
}
package com.security.securitydemo.dao;
import com.security.securitydemo.entity.RoleEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
/**
* @Author WQY
* @Date 2019/11/25 17:10
* @Version 1.0
*/
public interface Role_Jpa extends JpaRepository<RoleEntity,Long> {
@Query(value = "select d from RoleEntity d where d.id=?1")
RoleEntity selectById(Long id);
}
基础项写完以后就要与sprinsecurity结合了
springsecurity中对于用户的表示有自己的实体类相对应,它就是UserDetails,所以我们编写一个AuthUser实现这个接口:
package com.security.securitydemo.security.entity;
import com.security.securitydemo.entity.RoleEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author WQY
* @Date 2019/11/26 14:52
* @Version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AuthUser implements UserDetails {
private String username;
private String password;
private List<RoleEntity> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.isEmpty() ? Collections.EMPTY_LIST :
// ROLE_ 是springsecurity对于角色的默认前缀,如果不加,验证会失败
(roles.parallelStream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList())); }
@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 true;
}
}
其中关于账号过期,enable全部设置为通过,接下来就是编写CustomUserDetailsService,返回我们自己的AuthUser:
package com.security.securitydemo.security.impl;
import com.security.securitydemo.dao.Role_Jpa;
import com.security.securitydemo.dao.User_Jpa;
import com.security.securitydemo.entity.UserEntity;
import com.security.securitydemo.security.entity.AuthUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collections;
/**
* @Author WQY
* @Date 2019/11/26 14:55
* @Version 1.0
*/
@Service
@Primary
public class CustomUserDetailsServiceImpl implements UserDetailsService {
@Resource
private User_Jpa userRepository;
@Resource
private Role_Jpa roleRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("user: " + username + " is not found.");
}
return new AuthUser(user.getUsername(), user.getPassword(), roleRepository.findAllById(Collections.singletonList(user.getAuthority())));
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
System.out.println(bCryptPasswordEncoder.encode("123456"));
}
}
到此,其实我们的UserDetails已经注入了Spring中,其实我们加上@Service和@Primary之后,springsecurity就能自己检测到这个bean,然后作为自己的UserDetailsService。
因为加了加密,所以先用main方法对123456加密,然后放入数据库
最后是一个修改controller
package com.security.securitydemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author WQY
* @Date 2019/11/27 9:31
* @Version 1.0
*/
@Controller
@RequestMapping
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "/hello.html";
}
@RequestMapping("/login")
public String login() {
return "/login.html";
}
@RequestMapping("/index")
public String index() {
return "/index.html";
}
}
然后在访问页面输入admin和123456(自己定义以后的)
就会进入hello.html页面,由于在
SecurityConfig
中配置了几个放行的页面所以直接访问http://localhost:8880/index也行
因为他不需要权限
项目地址:https://download.csdn.net/download/qq_41755287/12000491