本次踩坑長達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控制
