Spring
Security
login
Annotation
email
Learn More →
DATASOURCE_HOST={資料庫主機}
DATASOURCE_PORT={資料庫埠號}
DATASOURCE_CATALOG={資料庫名稱}
DATASOURCE_USERNAME={資料庫帳號}
DATASOURCE_PASSWORD={資料庫密碼}
DATASOURCE_URL=postgresql://localhost:5432/{資料庫名稱}?password={密碼}&sslmode=prefered&user={使用者名稱}
DEVTOOLS_RESTART_ENABLED=true
PORT=8080
SPRING_PROFILES_ACTIVE=d
MAIL_USER={寄件者帳號}
MAIL_PASSWORD={寄件者密碼}
FACEBOOK_APP_ID={FACEBOOK應用程式編號}
FACEBOOK_APP_SECRET={FACEBOOK金鑰}
若使用 Gmail 或 G Suite 寄送電子郵件可能會寄送失敗,解決方法:https://support.google.com/accounts/answer/6010255
@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.
authorizeRequests().
antMatchers("/criteria","/registration/", "/registration/confirm", "/registration/resendToken", "/talent/forgetPassword", "/signin/**", "/signup/**").permitAll(). // 允許所有人請求
//antMatchers().hasAuthority("CHANGE_PASSWORD_PRIVILEGE").
//anyRequest().hasAnyRole("ADMIN", "USER"). // 其它全部的路徑都得經過使用者驗證後才可以存取
and().
formLogin(). // 使用 Form Login 登入驗證
loginPage("/login.aspx"). // 自定義登入頁面
permitAll(). // 允許所有人請求
//.loginProcessingUrl("/login") // 對應自定義登入頁面的 action URI
//defaultSuccessUrl("/"). // 登入成功後導向的URI
and().
logout().
// 如開啟CSRF功能, 會將 logout 預設為 POST, 在此設定使用任何 HTTP 方法請求(不建議)
//logoutRequestMatcher(new AntPathRequestMatcher("/logout")).
logoutUrl("/logout.aspx"). // 登出的 URL
//logoutSuccessUrl("/login"). // 登出後的跳轉地址(預設值原為 /login?logout)
permitAll().
invalidateHttpSession(true). // 登出時是否 invalidate HttpSession
deleteCookies("JSESSIONID"). // 登出同時清除 cookies
and().
rememberMe().
rememberMeParameter("remember"). //from的忘記我欄位name
key("uniqueAndSecret"). //組成 cookie 的加密字串
rememberMeCookieName("remember").
tokenValiditySeconds(86400).//設定有效時間 ,預設是兩周,這裡設置為1天
and().
sessionManagement().
sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).//設定Session策略
invalidSessionUrl("/login.aspx?session").//Session失效,重定向到可配置的URL
//典型session修復攻擊的保護-Session fixation
sessionFixation().
migrateSession().
and().
csrf().disable(); // 關閉 CSRF 防護
// 預設開啟 CSRF 功能, 需設定 csrfTokenRepository() 以存取 CsrfToken 進行驗證
//csrfTokenRepository(new HttpSessionCsrfTokenRepository());
}
// service/MyUserDetailsService.java
/**
* 實作 UserDetailsService 進行會員登入邏輯判斷
*/
@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private TalentRepository talentRepository;
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Talent talent = talentRepository.findByEmail(email);
if (talent == null) {
throw new UsernameNotFoundException("No user found with username: " + email);
}
boolean enabled = true; //啟用
/*
加入使用者登入資訊
*/
return new org.springframework.security.core.userdetails.User(talent.getEmail().toLowerCase(),
talent.getShadow(), enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked,
getAuthorities(talent.getRole()));
}
private static List<GrantedAuthority> getAuthorities(String role) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(role));
return authorities;
}
}
/**
* 新增使用者
*/
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
// 手動建立使用者
auth.inMemoryAuthentication()
.withUser("powerfish0813@gmail.com") //設定使用者帳號
.password(new BCryptPasswordEncoder().encode("123"))//設定密碼
.roles("ADMIN");//設定權限(可以同時給多個身分,用逗號隔開)
// 導入登入邏輯
auth.userDetailsService(userDetailsService);
}
//validator/PasswordMatches.java
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchesImpl.class)
@Documented
public @interface PasswordMatches {
String message() default "Passwords don't match";//設定驗證失敗回傳訊息
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
//validator/PasswordMatchesImpl.java
public class PasswordMatchesImpl implements ConstraintValidator<PasswordMatches, Object> {
/*
初始化
*/
@Override
public void initialize(PasswordMatches constraintAnnotation) {
}
/*
驗證邏輯
*/
@Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
RegistrationDto registration = (RegistrationDto) obj;
return registration.getPassword().equals(registration.getMatchingPassword());
}
}
// dto/RegistrationDto.java
/*
當初設定可套用類型TYPE, ANNOTATION_TYPE
故只有 class, interface, enum, annotation 可以套用此標籤
*/
@PasswordMatches
public class RegistrationDto {
@NotNull
@NotEmpty
private String nickName;
@NotNull
@NotEmpty
private String password;
private String matchingPassword;
@Pattern(regexp = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message = "Invalid email")
@NotNull
@NotEmpty
private String email;
// standard getters and setters
}
/**
* 認證(authentication)商業邏輯
* src/main/java/com/security/demo/service/UserDetailsServiceImpl.java
*/
@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SomeoneRepository someoneRepository;
@Autowired
private LoginAttemptService loginAttemptService;
@Autowired
private HttpServletRequest request;
/**
*
* @param email
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
String ip = getClientIP();
if (loginAttemptService.isBlocked(ip)) {
throw new RuntimeException("blocked");
}
Someone someone = someoneRepository.findOneByEmail(email);
if (someone == null) {
throw new UsernameNotFoundException("No user found with username: " + email);
}
boolean verfied = someone.isVerified();
//boolean accountNonExpired = true;
//boolean credentialsNonExpired = true;
//boolean accountNonLocked = true;
/*
加入使用者登入資訊
*/
return new org.springframework.security.core.userdetails.User(
someone.getEmail().toLowerCase(),
someone.getShadow(),
verfied,
true, // accountNonExpired
true, // credentialsNonExpired
true, // accountNonLocked
getAuthorities(someone.getRole())
);
}
/**
*
* @param role
* @return
*/
private static List<GrantedAuthority> getAuthorities(String role) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority(role));
return grantedAuthorities;
}
private final String getClientIP() {
final String header = request.getHeader("X-Forwarded-For");
if (header == null || header.isEmpty()) {
return request.getRemoteAddr();
}
return header.split(",")[0];
}
}
// java/WebSecurityConfigurer.java
@Override
@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
/*
登入邏輯 service/MyUserDetailsService.java
*/
@Autowired
private MyUserDetailsService userDetailsService;
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
// controller/RegistrationController.java
/**
* 建立會員
*
* @param registrationDto 註冊DTO
* @param result 驗證是否正確
* @return
*/
private Talent createUserAccount(RegistrationDto registrationDto, BindingResult result) {
Talent registered = null;
try {
registered = iUserService.registerNewUserAccount(registrationDto);
} catch (Exception e) {
return null;
}
return registered;
}
/**
* 取得註冊DTO
*
* @param nickName 暱稱
* @param email 信箱
* @param password 密碼
* @param matchingPassword 再輸入密碼
* @return
*/
public RegistrationDto testModeelAttribute(@RequestParam("nickName") String nickName, @RequestParam("email") String email, @RequestParam("password") String password, @RequestParam("matchingPassword") String matchingPassword) {
RegistrationDto registrationDto = new RegistrationDto();
registrationDto.setNickName(nickName);
registrationDto.setEmail(email);
registrationDto.setPassword(password);
registrationDto.setPassword(matchingPassword);
return registrationDto;
}
/**
* 註冊會員
*
* @param registrationDto 會員資料驗證
* @param result 驗證是否正確
* @param request
* @param errors
* @return
* @throws Exception
*/
@PostMapping("/")
public ModelAndView
registerUserAccount(@ModelAttribute("testModeelAttribute") @Valid RegistrationDto registrationDto, BindingResult result, WebRequest request, Errors errors) throws Exception {
Talent talent = new Talent();
/*
註冊DTO驗證
*/
if (!result.hasErrors()) {
/*
建立會員
*/
talent = createUserAccount(registrationDto, result);
}
if (talent == null) {
result.rejectValue("email", "message.regError");
}
if (result.hasErrors()) {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse("classpath:/skeleton/index.xml");
ModelAndView modelAndView = new ModelAndView("registrations");
modelAndView.getModelMap().addAttribute(new DOMSource(document));
return modelAndView;
}
try {
/*
執行註冊事件-寄送驗證信
*/
String appUrl = request.getContextPath();
applicationEventPublisher.publishEvent(new OnRegistrationCompleteEvent(talent, request.getLocale(), appUrl));
} catch (Exception me) {
System.out.print(me);
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse("classpath:/skeleton/index.xml");
ModelAndView modelAndView = new ModelAndView("registrations111");
modelAndView.getModelMap().addAttribute(new DOMSource(document));
return modelAndView;
}
return index();
}
// event/OnRegistrationCompleteEvent.java
/*
* 需要繼承 ApplicationEvent
*/
public class OnRegistrationCompleteEvent extends ApplicationEvent {
private static final long serialVersionUID = -6411093423884992233L;
private String appUrl;//驗證路徑
private Locale locale;//地區
private Talent talent;//會員資料
public OnRegistrationCompleteEvent(Talent talent, Locale locale, String appUrl) {
super(talent);
this.talent = talent;
this.locale = locale;
this.appUrl = appUrl;
}
// standard getters and setters
}
// listener/Listener
/*
* 需要實作ApplicationListener<註冊事件>
*/
@Component
public class RegistrationListenerImpl implements ApplicationListener<OnRegistrationCompleteEvent> {
@Autowired
private IUserService iUserService;
@Autowired
private MessageSource messageSource;
@Autowired
private JavaMailSender javaMailSender;
/*
當註冊事件被觸發的時候會觸發此方法
*/
@Override
public void onApplicationEvent(OnRegistrationCompleteEvent event) {
this.confirmRegistration(event);
}
private void confirmRegistration(OnRegistrationCompleteEvent event) {
Talent talent = event.getTalent();
UUID token = UUID.randomUUID();
/*
產生token
*/
iUserService.createVerificationToken(talent, token);
String recipientAddress = talent.getEmail();
String subject = "Registration Confirmation";
String confirmationUrl = event.getAppUrl() + "/registration/confirm?token=" + token.toString();
/*
發送E-mail
*/
SimpleMailMessage email = new SimpleMailMessage();
email.setTo(recipientAddress);
email.setSubject(subject);
email.setText("來認證喔:" + "http://localhost:8080" + confirmationUrl);
javaMailSender.send(email);
}
}
// controller/RegistrationController.java
@GetMapping("/confirm")
public ModelAndView confirmRegistration(WebRequest request, @RequestParam("token") UUID token) throws Exception {
Locale locale = request.getLocale();
/*
驗證Token是否正確
*/
VerificationToken verificationToken = iUserService.getVerificationToken(token);
if (verificationToken == null) {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse("classpath:/skeleton/index.xml");
ModelAndView modelAndView = new ModelAndView("registrations");
modelAndView.getModelMap().addAttribute(new DOMSource(document));
return modelAndView;
}
Talent talent = verificationToken.getTalent();
Calendar cal = Calendar.getInstance();
if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse("classpath:/skeleton/index.xml");
ModelAndView modelAndView = new ModelAndView("registrations111");
modelAndView.getModelMap().addAttribute(new DOMSource(document));
return modelAndView;
}
talent.setEnabled(true);
iUserService.saveRegisteredUser(talent);
return index();
}
@GetMapping("/resendToken")
public ModelAndView resendRegistration(WebRequest request, @RequestParam("token") UUID token) throws Exception {
VerificationToken verificationToken = iUserService.resetVerificationToken(token);
/*
觸發寄送驗證信事件
*/
try {
String appUrl = request.getContextPath();
applicationEventPublisher.publishEvent(new OnRegistrationCompleteEvent(verificationToken.getTalent(), request.getLocale(), appUrl));
} catch (Exception me) {
return index();
}
return new ModelAndView("redirect:/");
}
前往 Spring Security Acl
前往 Spring Boot Redis
前往 Spring Security防止暴力破解身份驗證
前往 Internationalization
一、建立檔案 .gitignore //不上傳檔案 二、指令 git --version //檢查版本 git init //初始化git git add [--all] //加入檔案 git add -f [filename] //強制加入檔案 git commit -m "mag" //建立儲存檔 git config user.name "name" //使用者名稱
Feb 22, 2021SQL MongoDB 說明 database database 資料庫 table collection
Oct 7, 2020專案架構 com.zygroup.ilnd config CustomerConfig 第一個資料庫Config ProductConfig 第二個資料庫Config model 第一個資料表
Oct 5, 2020/pom.xml <!-- https://github.com/google/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.1-jre</version> </dependency> Guava is a set of core Java libraries from Google that includes new collection types (such as multimap and multiset), immutable collections, a graph library, and utilities for concurrency, I/O, hashing, caching, primitives, strings, and more! It is widely used on most Java projects within Google, and widely used by many other companies as well. https://github.com/google/guava
Mar 5, 2020or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up