前言
Spring Security作为基于Spring的安全框架,具有身份认证,权限判别的功能。
一般而言,当你选用的是SSM,则选用shiro框架;选用Spring Boot,则使用Spring Security。
接口
从Spring Security的使用层面,下面介绍几大接口和类
1.类WebSecurityConfigurerAdapter
从名字上看,我们知道这是一个配置类,当我们需要使用Spring Security的各项功能时,我们需要继承该类,通过该类中的configure方法去配置程序需要进行权限管理的路径接口,认证成功或者失败需要跳转的处理器等功能。
下面是我项目中的一个例子(前后端分离)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)//开启Controller权限注解必备
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationSuccess authenticationSuccess;
@Autowired
private AuthenticationFailure authenticationFailure;
@Autowired
private MyAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private MyAccessDeniedHandler accessDeniedHandler;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //自定义编写登录页面
.usernameParameter("username").passwordParameter("password")
.loginProcessingUrl("/chat/login") //登录跳转到哪个Controller.生效,删除报错,不删除,前端拿不到返回信息
.successHandler(authenticationSuccess)
.failureHandler(authenticationFailure)
.and()
.authorizeRequests()//认证请求
.anyRequest().authenticated()
.and().csrf().disable()
.cors()
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
@Bean
PasswordEncoder password() {
return new BCryptPasswordEncoder();
}
/**
* 跨域配置
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("http://localhost:3000");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
解释:
首先我们先使用注解@Configuration将该配置类注入到Spring容器中。
接着我们其他方法先不看,重点关注protected void configure(HttpSecurity http) throws Exception {}
方法:
1.该方法中首先使用formLogin方法,代表使用我们自己的登录界面,(Spring Security有默认的登录界面。)
2.后接.usernameParameter("username").passwordParameter("password") .loginProcessingUrl("/chat/login")
表示我们登录界面表单中用户名,密码的配置项的key值。(注意:SpringSecurity只接收application/x-www-form-urlencoded格式的数据,本项目前端用Antd表单默认格式为application/json,搞了好久才找到这个原因!!!)
3..loginProcessingUrl("/chat/login")
方法用于跳转登录验证的后端接口
4. 下面的四个方法分别是设定登录验证成功跳转的处理器,失败跳转的处理器,未登录跳转的处理器和无权限访问跳转的处理器。后面会挑一两个讲讲,其他的也就一样。
.successHandler(authenticationSuccess)
.failureHandler(authenticationFailure)和
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
2.接口UserDetailsService
程序中设置一个类继承该接口,在该类中设置具体的比对逻辑,该接口用于框架比对配置中或者和数据库中的用户名密码是否一致。
例子:
该例子通过获取数据库中的用户名和密码,与用户输入的用户名进行比对,查看数据是否存在,如果存在,则获取其权限传参。
/**
* 通过用户名判断是否存在该数据,返回数据库中该用户名的密码
*/
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo user = userMapper.getUserByName(username);
String userName = user.getUsername();
//此处将数据库中查询出来的密码进行加密,已便config中用加密的密码进行认证
//String password = new BCryptPasswordEncoder().encode(user.getPassword());
String password = user.getPassword();
String role = user.getRole();
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
//设置用户权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList(role);
return new User(userName,password,auths);
}
}
接着在WebSecurityConfigurerAdapter中运用下面的configure方法进行username和password的比对。
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
另外:model中的用户需要实现UserDetails接口
3.接口AuthenticationFailureHandler
该接口主要用来编写登录失败后应该如何处理的逻辑,具体例子如下:
由于我的项目是前后端分离,每次登录成功还是失败,都需要向前端返回相应的数据,让前端根据接收到的json数据串进行相应页面显示。该例子中ResponseJson是我自己编写的vo类,用于设置返回状态码和相应数据。在该方法中如果登录失败,则将相应信息写入json中返回。
登录成功处理器也大概类似这种写法。
@Component
public class AuthenticationFailure implements AuthenticationFailureHandler {
private final Logger logger = LoggerFactory.getLogger(AuthenticationFailure.class);
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
//httpServletResponse.setStatus(500);
System.out.println("登录失败");
ResponseJson responseJson = new ResponseJson().error("用户名或者密码错误");
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.write(objectMapper.writeValueAsString(responseJson));
printWriter.flush();
printWriter.close();
}
}
4.接口AccessDeniedHandler
该接口用户判断用户是否有访问某个接口的权限。
例子:
和其他处理器一样,都需要实现方法完成业务逻辑。
/**
* 权限处理类
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("无权限访问");
ResponseJson responseJson = new ResponseJson().error("无权限访问");
response.setContentType("application/json;charset=UTF-8");
PrintWriter printWriter = response.getWriter();
printWriter.write(objectMapper.writeValueAsString(responseJson));
printWriter.flush();
printWriter.close();
}
}
但是,我们如何操作在哪些特定接口才进行权限判断呢?
我们需要在Controller中用@PreAuthorize(“hasAuthority(‘admin’)”)开启对某个接口的权限判定
/**
* 模糊查询信息(分页查询)
*
* @return
*/
@CrossOrigin(origins = "*", maxAge = 3600)
@RequestMapping(value = "/usersearchlist",method = RequestMethod.POST)
@ResponseBody //用于转换对象为JSON
@PreAuthorize("hasAuthority('admin')")
public ResponseJson usersearchlist(@RequestBody Map<String, String> data) {
String key = data.get("name");
List<UserInfo> list = chatService.userfindkeyAll(key);
if(list.size()!=0){
return new ResponseJson().success().setData("datalist", list);
}return new ResponseJson().error("查找失败");
}
注意:hasAuthority不需要在“admin”加前缀ROLE_;hasRole需要
最后在WebSecurityConfigurerAdapter中加入注解
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)//开启Controller权限注解必备
方可使用
最后
Spring Security中还有接口和类,比如:使用AuthenticationProvider实现自定义认证流程,使用SecurityContext可以获取和设置当前的认证对象等等
但本文基于使用的方面上说,上述几个接口已经足够实现基本功能,其他功能本项目无要求,所以没有使用,也就不谈了。有兴趣自己再研究研究叭。