JDBC用户存储
依赖
<!--数据库连接-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
properties
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql:///spring_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
建表
Spring Security默认情况下需要两张表,用户表和权限表
参考mysql
create table users(username varchar(50) not null primary key,password varchar(500) not null,enabled boolean not null);create table authorities (username varchar(50) not null,authority varchar(50) not null,constraint fk_authorities_users foreign key(username) references users(username));create unique index ix_auth_username on authorities (username,authority);
实现
@Resource
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
JdbcUserDetailsManager manager = auth.jdbcAuthentication()
.dataSource(dataSource)
.getUserDetailsService();
if (manager.userExists("zhow")) {
System.out.println("已注册");
} else {
manager.createUser(User.withUsername("zhow")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("admin")
.build()
);
}
}
或者
@Resource
DataSource dataSource;
@Bean
public UserDetailsService userDetailsService() {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
if (manager.userExists("zhow")) {
System.out.println("已注册");
} else {
manager.createUser(User.withUsername("zhow")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("admin")
.build()
);
}
return manager;
}
查询用户
自定义用户登录查询
新建一个service实现UserDetailsService
接口
@Service
public class UserService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 模拟数据库查询
System.out.println("开始查询数据源。。。");
if("zhow".equals(username)) {
return User.withUsername("zhow")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("admin")
.build();
}else {
throw new BadCredentialsException("没了");
}
}
}
自定义用户权限校验
校验器
public class MyAuthprovider implements AuthenticationProvider {
//@Resource
//注入不进去
private UserService userService;
public MyAuthprovider(UserService userService) {
this.userService = userService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 密码校验
System.out.println("开始自定义验证~~~~");
System.out.println(authentication);
//查询用户名
UserDetails userDetails = userService.loadUserByUsername("xxx");
// 密码加密器
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 密码加密
String encodePass = passwordEncoder.encode(authentication.getCredentials().toString());
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, encodePass, userDetails.getAuthorities());
return authenticationToken;
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
配置校验器
auth.authenticationProvider(new MyAuthprovider(userService));
这里有一个大坑,UserService
在MyAuthprovider中注入不进去,希望有知道原因的大神告诉一下我原因,万分感谢!
我使用在connfig中注入,通过构造方法去传到校验器中中。
同一用户多地点登录
踢掉其他已登录的用户
http.
// 哪些 地址需要登录
authorizeRequests()
//所有请求都需要验证
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()
.maximumSessions(1);
禁止其他终端登录
http.
// 哪些 地址需要登录
authorizeRequests()
//所有请求都需要验证
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
注销登录
开启CSRF之后 需要使用post请求退出接口
<a href="/logout">GET logout</a>
<br />
<form action="/logout" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="submit" value="POST Logout"/>
</form>
默认方式 get /logout
自定义url
.and()
.logout()
.logoutUrl("/out")
增加退出处理器
http.logout().logoutUrl("/logout")
.addLogoutHandler((request, response, authentication) -> {
// TODO Auto-generated method stub
System.out.println("退出1");
})
.addLogoutHandler((request, response, authentication) -> {
// TODO Auto-generated method stub
System.out.println("退出2");
});
登录成功处理器
不同角色 跳转到不同页面
.successHandler((request, response, authentication)-> {System.out.println("登录成功1");
// 根据权限不同,跳转到不同页面
Enumeration<String> attributeNames = request.getSession().getAttributeNames();
while (true){
if (attributeNames.hasMoreElements()){
System.out.println( request.getSession().getAttribute(attributeNames.nextElement()));
}else {
break;
}
}
request.getRequestDispatcher("").forward(request, response);
});
其中 Authentication 参数包含了 用户权限信息
登录失败处理器
.failureHandler((httpServletRequest, httpServletResponse, e)-> {
e.printStackTrace();
})
可以限制登录错误次数