Spring Security 学习之旅开始
SpringSecurity 开始
我的项目:Github
1. 引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
在 manven 依赖仓库中:
2. 配置 Security
1. 在包下创立 SecurityCconfig 类, 重写 configure 办法,其中 WebSecurity web,能够定义疏忽门路
@Override
public void configure(WebSecurity web) throws Exception {
// 疏忽拦挡
web.ignoring().antMatchers("/sayHello","/doLogin");
}
- HttpSecurity http 能够拦挡申请, 能够定义登录、登出等等
@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 开启登录
// 示意拜访,ex/index 这个接口,须要具备 admin 角色
.antMatchers("/es/**").hasRole("admin")
// 示意残余的其余接口,登录之后能拜访
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
// 登录解决接口
.loginProcessingUrl("/doLogin")
// 定义登录时,用户名的 key,默认为 username
.usernameParameter("username")
// 定义登录时,用户明码的 key,默认为 password
.passwordParameter("password")
// 定义登录胜利的处理器
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");
response.sendRedirect("/success.html");// 重定向到一个页面
MyUserDetails detail= (MyUserDetails)authentication.getPrincipal();
System.out.println(detail);
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
ResponseBean responseBean = ResponseBean.sendByCode("you have login failure !", 401);
String result = new ObjectMapper().writeValueAsString(responseBean);
out.write(result);
out.flush();}
})
// 和表单登录相干的接口通通都间接通过
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("you have login out success !");
out.flush();}
})
.permitAll()
.and()
.httpBasic()
.and()
.csrf().disable();
}
简略的表单登录配置,这里的 logou 是 Get 申请,若要 Post 申请,则减少一行
logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
and 相当于 ssm 中标签的完结,permitAll 示意登录相干的页面、操作不要拦挡。
- 定义明码加密
因为 security 自带盐,用明文加密的都不一样,省去了咱们很多工夫。
@Bean
public PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();
}
- 引入 JPA 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
- 创立 Use 类
@Data
@Entity(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private Integer enabled;
private Integer locked;
}
- 创立 Role 类,为 MyDetail 作筹备
@Data
@Entity(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String chineseName;
}
- 创立 MyDetail 类,实现 UserDetail
@Data
public class MyUserDetails implements UserDetails {
private User user;
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {List<SimpleGrantedAuthority> authorities = new ArrayList<>();
if (roles != null && roles.size() > 0) {for (Role role : roles) {authorities.add(new SimpleGrantedAuthority(role.getName()));
}
}
return authorities;
}
@Override
public String getPassword() {return user == null ? null : user.getPassword();
}
@Override
public String getUsername() {return user == null ? null : user.getUsername();
}
@Override
public boolean isAccountNonExpired() {return true;}
@Override
public boolean isAccountNonLocked() {Integer locked = user.getLocked();
if (locked == 0) {return true;}
return false;
}
@Override
public boolean isCredentialsNonExpired() {return true;}
@Override
public boolean isEnabled() {Integer enabled = user.getEnabled();
if (enabled == 1) {return true;}
return false;
}
@Override
public String toString() {
return "MyUserDetails{" +
"user=" + user +
", roles=" + roles +
'}';
}
}
- 编写注入实体办法
在测试类中,创建对象,jpa 会主动去创建表格。做一个示范,因为学习,我先创立的表格,你们能够少走弯路了。比方上面这样:
实体,加注解
@Data
@Entity(name = "t_hill_heavy")
public class HillHeavy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private Boolean handsome;
private String gender;
private Integer high;
private boolean rich;
创立 Dao
public interface HillHeavyDao extends JpaRepository<HillHeavy,Integer> {
/**
* 查问
* @param username 用户名
* @author 山沉
* @date 2020/12/28 22:42
* @return {@link HillHeavy}
*/
HillHeavy findHillHeavyByUsername(String username);
}
在测试类中:
@SpringBootTest
class EsSearchApplicationTests {private static final Logger logger = LoggerFactory.getLogger(EsSearchApplicationTests.class);
@Resource
private HillHeavyDao hillHeavyDao;
@Test
void contextLoads() {HillHeavy hillHeavy = new HillHeavy();
hillHeavy.setUsername("山沉");
hillHeavy.setHandsome(true);
hillHeavy.setHigh(180);
hillHeavy.setGender("男");
hillHeavy.setRich(true);
hillHeavyDao.save(hillHeavy);
logger.info("实体 ----->{}",hillHeavy);
}
}
这样在用 dao 层去注入实体,在数据库中,也会生成表格,数据。如下:
是有点自恋,啊。原谅想帅的小瘦子。
- 有了表构造,在 UserServiceImpl 类,去实现 UserDetailService 类,重写
loadUserByUserName(String username)
在此办法中,从数据库中依据 username,查问出用户,角色,返回 UserDetail 对象。
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userMapper.selectUser(username);
MyUserDetails details = new MyUserDetails();
if(user == null){throw new BadCredentialsException("this username or password is not true!");
}
details.setUser(user);
Integer id = user.getId();
List<Role> roles = userMapper.selectRole(id);
details.setRoles(roles);
return details;
}
3. 测试登录
到此,咱们的表单登录,就胜利了。通过 doLogin 登录,携带 user 信息,进入 UsernamePasswordAuthenticationFilter
中,注入用户信息。在从表中取得用户信息与之比照,而后通过登录胜利或失败返回给前端 JSON 格局。
在这里说下,successHandler
是比拟弱小的,在外面能够作重定向,也能够获取用户信息,等等。集成 defaultSuccessUrl() successForwardUrl()
。
defaultSuccessUrl
能够指定登录胜利的跳转页面,比方输出welcome/say
, 来到login.html
页面。登录胜利后,会来的welcome/say
。sccessForwardUrl
指定登录胜利后,到那里。不论登录前你操作的哪一个接口,胜利后一律到指定的门路。
测试登录