SpringSecurity详解
- 登录实现:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class userDetailService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("*******************************");
//1.根据用户名去数据库查询,不存在则抛出异常UsernameNotFoundException
if (!"admin".equals(username)) {
throw new UsernameNotFoundException("用户名不存在");
}
//2.比较密码(注册时已经加密) 如果成功返回userDetails
String password = passwordEncoder.encode("123");
return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
}
}
- 通过配置进行权限控制
import com.song.handler.MyAccessDeniedHandler;
import com.song.handler.MyAuthenticationFailureHandler;
import com.song.handler.MyAuthenticationSuccessHandler;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
//自定义登陆页面
http.formLogin()
//自定义入参
.usernameParameter("username11")//Spring默认的都是username,如果想更换则需要配置usernameParameter,不配置会报错
//自定义登陆页面
.loginPage("/login.html")
//必须和表单提交的接口一样,执行自定义登录逻辑
.loginProcessingUrl("/login")
//登陆成功跳转
//.successForwardUrl("/toLogin")
//自定义登陆成功处理器(源码中是转发,而我们自定义使用的是重定向)
.successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))
//自定义登陆失败处理器
.failureHandler(new MyAuthenticationFailureHandler("http://www.baidu.com"));
//.failureForwardUrl("/toError");
//授权
http.authorizeRequests()
//放行,不需要认证
.antMatchers("/login.html").permitAll()
.antMatchers("/error.html").permitAll()
//基于权限控制,严格区分大小写
//.antMatchers("/main1.html").hasAuthority("admin")
.antMatchers("/main1.html").hasAnyAuthority( "adN")
//所有的请求都要认证,必须登录(anyRequest需要放在最后,否则会直接报错)
//基于角色判断(需要注意 在授权的时候需要写ROLE_xxx)认证的时候就是abc
.antMatchers("/main1.html").hasRole("abc")
//基于IP地址
//.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
.anyRequest().authenticated();
//异常处理
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);
//关闭csrf防护
http.csrf().disable();
}
@Bean
public PasswordEncoder getPw() {
return new BCryptPasswordEncoder();
}
-
通过注解进行权限控制
需要开启注解,因为Spring默认是关闭注解进行权限控制的 @EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法注解 在启动类前加或者在SecurityConfig配置类里面加 Secured 在角色控制的时候必须使用ROLE_xxx (以Role开头)
-
自定义403页面
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; @Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { //自定义403页面 response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setHeader("Content-Type", "application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write("{\"status\":\"error\",\n \"msg:\" \"权限不足,请联系管理员\"}"); writer.flush(); writer.close(); } }
-
配置rememberme
-
导入依赖
<!--使用SpringSecurity中的 RememberMe 需要导入 mybatis 和 mysql 数据库依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency>
-
配置Jdbc
spring: datasource: url: "jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC" username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
-
security配置
import com.song.handler.MyAccessDeniedHandler; import com.song.service.UserDetailServiceImpl; import javax.sql.DataSource; 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.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解控制权限 @Autowired private DataSource dataSource; @Autowired private PersistentTokenRepository persistentTokenRepository; @Autowired private UserDetailServiceImpl userDetailService; @Override protected void configure(HttpSecurity http) throws Exception { //配置RememberMe http.rememberMe() .tokenRepository(persistentTokenRepository) //超时时间,默认2周 .tokenValiditySeconds(60) //自定义登录逻辑 .userDetailsService(userDetailService); //退出登录 http.logout() //退出成功后跳转的页面 .logoutSuccessUrl("/login.html") .logoutUrl("/logout"); } @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); //设置数据源 jdbcTokenRepository.setDataSource(dataSource); //自动建表(第一次启动时开启,第二次启动时注释掉,因为第一次建表,第二次存在就报错) // jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; } ********************************************************************************* import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; 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.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service public class UserDetailServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("来来来"); //1.根据用户名去数据库查询,不存在则抛出异常UsernameNotFoundException if (!"admin".equals(username)) { throw new UsernameNotFoundException("用户名不存在"); } //2.比较密码(注册时已经加密) 如果成功返回userDetails String password = passwordEncoder.encode("123"); return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc")); } }
-