本次踩坑长达4小时...由于感觉以xml配置的方式来配置security不太灵活, 于是开始了踩坑之旅.
一. 引入Maven依赖
<!--
<spring.version>4.2.4.RELEASE</spring.version>
-->
<!--Spring Security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
二. 创建加载用户信息的类,实现UserDetailsService接口
package com.xxxx.xxx.xxx.impl;
import com.usoft.scenery.mapper.AdminRoleMapper;
import com.usoft.scenery.pojo.AdminUser;
import com.usoft.scenery.pojo.Role;
import com.usoft.scenery.pojo.vo.AdminRole;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 根据用户名从数据库获取对应的管理员信息,从信息中取出角色列表并添加到授权中
*
* @author Braycep
* @date 2019/3/6 12:53
*/
@Service(value = "userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource(name = "adminRoleMapper")
private AdminRoleMapper adminRoleMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
LoggerFactory.getLogger(UserDetailsServiceImpl.class).info("用户:username=" + s + " 请求授权");
AdminRole adminRole = adminRoleMapper.findOne(s);
if (adminRole != null) {
List<AdminUser> adminUser = adminRole.getAdminUsers();
List<Role> role = adminRole.getRoles();
if (adminUser != null && adminUser.size() == 1) {
if (role != null && role.size() > 0) {
List<GrantedAuthority> list = new ArrayList<>();
for (Role r : role) {
list.add(new SimpleGrantedAuthority(r.getMark()));
}
AdminUser admin = adminUser.get(0);
if (admin != null && admin.getState() == 1) {
if (admin.getStartTime().getTime() < System.currentTimeMillis() && System.currentTimeMillis() < admin.getEndTime().getTime()) {
return new User(s, admin.getPassword(), list);
}
}
}
}
}
return null;
}
}
三. 创建配置类
package com.xxx.xxx.xxx.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.annotation.Resource;
/**
* Security的配置类
*
* @author Braycep
* @date 2019/3/6 14:09
*/
@Configuration
@EnableWebSecurity(debug = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//自定义的身份认证处理类,实现UserDetailsService接口
@Resource(name = "userDetailsService")
private UserDetailsService userDetailsService;
//数据库连接池,用于使用记住我的功能
@Autowired
private DruidDataSource dataSource;
//security自带的密码加密类
@Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
//用于对记住我这个功能生成token
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
//配置访问
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
//放行
.antMatchers("/fonts/**", "/css/**", "/images/**", "/js/**", "login.html").permitAll()
//角色 SU 可以访问"/html/**"
//.antMatchers("/html/**").hasRole("SU")
//已经认证过的就可以访问
.anyRequest().authenticated()
.and()
.headers()
//设置页面frame分区的策略
.frameOptions().sameOrigin()
.and()
//使用security的/login登录验证, loginProcessingUrl的值就是表单提交的地址
.formLogin().loginProcessingUrl("/login").loginPage("/login.html")
.defaultSuccessUrl("/index.html")
.failureForwardUrl("/login.html?failed")
.failureUrl("/login.html?failed")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html")
//记住密码
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())//设置操作表的Repository
.tokenValiditySeconds(60 * 60 * 24 * 7)//设置记住我的时间为7天
.userDetailsService(userDetailsService);//设置userDetailsService
// 加http.csrf().disable()是为了防止报这个错Whitelabel Error Page
//This application has no explicit mapping for /error, so you are seeing this as a fallback.
http.csrf().disable();
}
//设置密码加密的方式
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder());
}
}
四. 注册security配置
package com.xxx.xxx.xxx.config;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* @author Braycep
* @date 2019/3/6 15:31
*/
public class SecurityWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//2.1创建AnnotationConfigWebApplicationContext
AnnotationConfigWebApplicationContext springContext = new AnnotationConfigWebApplicationContext();
//2.2spring的配置类 springSecurity的配置类
springContext.register(WebSecurityConfig.class);
}
}
五. 创建初始化类, 必须的
package com.xxx.xxx.xxx.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* @author Braycep
* @date 2019/3/6 15:44
*/
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
//只继承就好
}
前端的代码注意表单的提交地址, 应该和loginProcessingUrl中的参数一致, 其余的html只是成功或者失败的跳转页面, loginPage是登录页面, 不是登录提交地址, 并且, 表单只能post提交; /logout这接口一旦请求就会退出登录, 并跳转到loginPage
如果要使用xml配置文件的方式来使用Security, 就需要在web.xml中去加载它, 这里不用在web.xml中加载
记住我功能的数据库表是固定的
create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null);
然后前端的复选框的属性name也要固定为 'remember-me'
<input name="remember-me" type="checkbox" title="记住密码">
数据库存储结果: 由Spring Security控制