<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
导包之后,就会在控制台生成一个账号和密码,密码是随机的,每次重启之后都不同,而且账号的权限也很高。
public class User implements UserDetails {
private int id;
private String username;
private String password;
private String salt;
private String email;
private int type;
private int status;
private String activationCode;
private String headerUrl;
private Date createTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
//账号未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//账号未锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
//凭证未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//账号可用
@Override
public boolean isEnabled() {
return false;
}
public void setUsername(String username) {
List<GrantedAuthority> list = new ArrayList<>();
this.username = username;
}
//调用这个方法可以知道该用户的权限
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<>();
list.add(new GrantedAuthority() {
@Override
public String getAuthority() {
switch (type){
case 1:
return "ADMIN";
default:
return "USER";
}
}
});
return list;
}
}
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
public User findUserByName(String username) {
return userMapper.selectByName(username);
}
//根据用户名查询用户
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.findUserByName(username);
}
}
@Controller
public class HomeController {
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model) {
// 认证成功后,结果会通过SecurityContextHolder存入SecurityContext中.
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (obj instanceof User) {
model.addAttribute("loginUser", obj);
}
return "/index";
}
@RequestMapping(path = "/discuss", method = RequestMethod.GET)
public String getDiscussPage() {
return "/site/discuss";
}
@RequestMapping(path = "/letter", method = RequestMethod.GET)
public String getLetterPage() {
return "/site/letter";
}
@RequestMapping(path = "/admin", method = RequestMethod.GET)
public String getAdminPage() {
return "/site/admin";
}
@RequestMapping(path = "/loginpage", method = {RequestMethod.GET, RequestMethod.POST})
public String getLoginPage() {
return "/site/login";
}
// 拒绝访问时的提示页面
@RequestMapping(path = "/denied", method = RequestMethod.GET)
public String getDeniedPage() {
return "/error/404";
}
}
这个SpringSecurity的配置类,主要的配置都在这个完成的:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
public void configure(WebSecurity web){
//忽略静态资源的访问
web.ignoring().antMatchers("/resources/**");
}
/**
* 处理认证逻辑
* AuthenticationManager : 认证的核心接口
* AuthenticationManagerBuilder : 用于构建AuthenticationManager对象的工具
* ProviderManager : AuthenticationManager接口的默认实现类
*/
public void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 自定义认证规则:和shiro的认证原理相同
* ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证
* 委托模式:让认证委托给AuthenticationProvider
*/
auth.authenticationProvider(new AuthenticationProvider() {
/**
*
* 获取认证信息:SpringSecurity做认证
* Authentication:用来封装认证信息的接口,不同的实现类代表不同类型的认证信息
* authentication:封装了前端登录传入的用户名和密码
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//获取username
String username = authentication.getName();
//获取password
String password = (String)authentication.getCredentials();
//根据用户名到数据库中查询用户信息
User user = userService.findUserByName(username);
if(user==null){
throw new UsernameNotFoundException("账号不存在");
}
password = CommunityUtil.md5(password+user.getSalt());
if(!user.getPassword().equals(password)){
throw new BadCredentialsException("密码不正确");
}
//返回认证的结果:主要信息,证书,权限
return new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
}
/**
* 当前AuthenticationProvider支持哪种认证类型
*/
@Override
public boolean supports(Class<?> aClass) {
//Authentication接口的常用实现类,即支持账号密码的认证
return UsernamePasswordAuthenticationToken.class.equals(aClass);
}
});
}
public void configure(HttpSecurity http) throws Exception{
//登录相关的配置
http.formLogin()
.loginPage("/loginpage")//登录页面
.loginProcessingUrl("/login")//处理登录请求的路径
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//重定向:重定向是客户端行为
//服务器A给浏览器做出相应后,浏览器再次发送一个请求到服务器B,两次不同的请求
response.sendRedirect(request.getContextPath()+"/index");
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
//将错误绑定到request上,转发到登录页面
//同一个请求,服务器A将一个请求转发给另一个服务器B处理
//转发是服务器行为
request.setAttribute("error",e.getMessage());
request.getRequestDispatcher("/loginpage").forward(request,response);
}
});
//登出相关的配置
http.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect(request.getContextPath()+"/index");
}
});
//授权配置
http.authorizeRequests()
.antMatchers("/letter").hasAnyAuthority("USER","ADMIN")//私信的权限
.antMatchers("/admin").hasAnyAuthority("ADMIN")
.and()
.exceptionHandling().accessDeniedPage("/denied");//没有权限时返回的页面
// 验证码配置:增加filter,验证码应该在验证用户名和密码之前进行验证
http.addFilterBefore(new Filter() {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if(request.getServletPath().equals("/login")){
String verifyCode = request.getParameter("verifyCode");
//假设我们的验证码固定为1234
if(verifyCode==null || verifyCode.equalsIgnoreCase("1234")){
request.setAttribute("error","验证码错误");
request.getRequestDispatcher("/loginpage").forward(request,response);
return;
}
}
//验证码正确,让请求继续向下执行
filterChain.doFilter(request,response);
}
}, UsernamePasswordAuthenticationFilter.class);
//记住我配置
http.rememberMe()
.tokenRepository(new InMemoryTokenRepositoryImpl())//将用户信息记录内存中(也可以数据库中)
.tokenValiditySeconds(3600*24)//token的过期时间
.userDetailsService(userService);//通过UserService查询用户信息,自动进行认证
}
}