1.在application.yml中添加起步依赖java
2.自定义安全类web
package com.example.demo.readinglist; import org.springframework.beans.factory.annotation.Autowired; 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.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private ReaderRepository readerRepository; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").access("hasRole('READER')") .antMatchers("/**").permitAll() .and() .formLogin() .loginPage("/login") .failureUrl("/login?error=true"); } @Override protected void configure( AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDetails userDetails = readerRepository.findOne(username); if (userDetails != null) { return userDetails; } throw new UsernameNotFoundException("User '" + username + "' not found."); } }); } }
3.定义实体类spring
package com.example.demo.readinglist; import java.util.Arrays; import java.util.Collection; import javax.persistence.Entity; import javax.persistence.Id; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @Entity public class Reader implements UserDetails { private static final long serialVersionUID = 1L; @Id private String username; private String fullname; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getFullname() { return fullname; } public void setFullname(String fullname) { this.fullname = fullname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Arrays.asList(new SimpleGrantedAuthority("ROLE_READER")); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
4.定义实体对应的仓库数据库
package com.example.demo.readinglist; import org.springframework.data.jpa.repository.JpaRepository; public interface ReaderRepository extends JpaRepository<Reader, String> { }
5.自定义参数解析器安全
package com.example.demo.readinglist; import org.springframework.core.MethodParameter; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; @Component public class ReaderHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return Reader.class.isAssignableFrom(parameter.getParameterType()); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Authentication auth = (Authentication) webRequest.getUserPrincipal(); return auth != null && auth.getPrincipal() instanceof Reader ? auth.getPrincipal() : null; } }
从安全认证返回的结果中得到参数实体类,其中为何能从安全认证的结果中获得实体类,后面会详细说明app
6.在主应用程序中添加视图控制器和参数解析器ide
package com.example.demo; import java.util.List; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.example.demo.readinglist.ReaderHandlerMethodArgumentResolver; @SpringBootApplication public class ReadingListApplication extends WebMvcConfigurerAdapter { public static void main(String[] args) { SpringApplication.run(ReadingListApplication.class, args); } //添加视图控制器 @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); } //添加自定义参数解析器 @Override public void addArgumentResolvers( List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new ReaderHandlerMethodArgumentResolver()); } }
大致流程:源码分析
Spring Boot在启动的时候会先扫描到自定义的安全配置类SecurityConfig,登录时会根据输入的用户与密码从嵌入式数据库中查找对应的记录,若是找到了则表示认证成功ui
详情以下:this
1.Spring Boot启动时扫描安全配置类SecurityConfig,并设置权限控制和认证策略
权限控制
认证策略,该方法会将匿名内部类注入到DaoAuthenticationProvider的userDetailsService
2.输入用户名和密码,点击登陆,Spring Boot 根据参数使用DaoAuthenticationProvider的retrieveUser方法获得登陆用户详情
圈出来的就是关键的部分,这里就是调用了匿名内部类中重写发loadUserByUsername方法
最终调用的就是根据用户名查找用户详情的代码(若是对Spring 的 repository高级特性不懂的再去百度一下)
而后使用DaoAuthenticationProvider类的additionalAuthenticationChecks进行密码的比较
认证经过建立一个UsernamePasswordAuthenticationToken,而且属性principal为一个UserDetails类型的Reader(属性已经所有赋值),则密码不对则抛出异常
若是应用的控制器须要使用上面获得的Reader,那么使用以下代码便可
Spring 判断是否是目标方法的参数是否是支持的参数类型(Reader.class),若是是,则从request中取登陆认证结果对象做为参数传给目标方法,Spring实际上就是在绑定参数时调用了ReaderHandlerMethodArgumentResolver.resolveArgument方法作到的