在元婴篇,学习了Spring Security,实现了不连接数据库进行授权验证,下面是Spring Security+JDBC查询数据库的实践
持久层实现类—findByLoginName方法
@Repositorypublic class UserDaoImpl implements UserDao{@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic User findByLoginName(String username) {String sql = "select * from user where username = '"+username+"'";return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class));}}
定义一个UserService类,实现UserDetailsService
注意点:返回的权限必须是“ROLE_”开头,比如权限是vip1,我返回告诉spring security的时候,必须告诉他权限是ROLE_vip1,这样spring security才会认为权限是vip1。
@Servicepublic class UserService implements UserDetailsService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println("进入service,用户名为 : " + username);if(username == null || username.equals("")){throw new UsernameNotFoundException("请输入用户名!");}List<SimpleGrantedAuthority> list = new ArrayList<>();User user = userDao.findByLoginName(username);for(String s : user.getRole().split(" ")){s = "ROLE_" + s;//由于不可能是空的(数据库中必须字段)list.add(new SimpleGrantedAuthority(s));System.out.println(s);}return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), list);}}
对Spring Security的架构和核心类进行高度的概括,他们分别是一个或者两个核心接口以及其实现类, Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider列表,每个AuthenticationProvider会轮流检查身份认证。检查后或者返回Authentication对象或者抛出异常。
大多数的身份认证提供程序都利用了UserDetails和UserDetailsService接口,身份认证最常用的方法是加载相应的UserDetails并检查加载的密码和用户输入的密码,UserService接口里面只有一个用来给用户获取信息的loadUserByUsername方法,返回的就是一个UserDetails对象,AuthenticationManager会加载这个对象进行检查。
SecurityConfig类
@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserService userService;//链式编程//授权@Overrideprotected void configure(HttpSecurity http) throws Exception {//首页所有人可以访问,功能页只有有对应权限的人才能访问//请求授权的规则http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3");//没有权限默认到登录页,需要开启登录的页面//usernameParameter和passwordParameter的参数固定为username和passwordhttp.formLogin().usernameParameter("username").passwordParameter("password");}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(new CustomPasswordEncoder());}}
CustomPasswordEncoder
CustomPasswordEncoder是自定义的继承自PasswordEncoder的密码编码类
public class CustomPasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {return rawPassword.toString();}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {return encodedPassword.equals(rawPassword);}}
正常加密与匹配的流程如下:
(1)加密(encode):注册用户时,使用SHA-256+随机盐+密钥把用户输入的密码进行hash处理,得到密码的hash值,然后将其存入数据库中。
(2)用户登录时,密码匹配阶段并没有进行密码解密(因为密码经过Hash处理,是不可逆的),而是使用相同的算法把用户输入的密码进行hash处理,得到密码的hash值,然后将其与从数据库中查询到的密码hash值进行比较。
因为数据库中的数据是事先存入的,没有进行加密,所以使用自定义类对密码编码的方法重写,通过直接比较内容来判断密码是否正确。
