自定义用户认证逻辑:
1,处理用户信息获取,2,用户校验,3密码的加密解密
新建:MyUserDetailService类,实现UserDetailsService接口。
UserDetailsService接口代码:只有一个方法,通过用户名获取用户信息,返回UserDetail
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
UserDetail:记录了 账户是否启用、账户是否过期、密码是否过期、账户是否锁定、权限集合信息
public interface UserDetails extends Serializable { // ~ Methods // ======================================================================================================== /** * Returns the authorities granted to the user. Cannot return <code>null</code>. * * @return the authorities, sorted by natural key (never <code>null</code>) */ Collection<? extends GrantedAuthority> getAuthorities(); /** * Returns the password used to authenticate the user. * * @return the password */ String getPassword(); /** * Returns the username used to authenticate the user. Cannot return <code>null</code> * . * * @return the username (never <code>null</code>) */ String getUsername(); /** * Indicates whether the user's account has expired. An expired account cannot be * authenticated. * * @return <code>true</code> if the user's account is valid (ie non-expired), * <code>false</code> if no longer valid (ie expired) */ boolean isAccountNonExpired(); /** * Indicates whether the user is locked or unlocked. A locked user cannot be * authenticated. * * @return <code>true</code> if the user is not locked, <code>false</code> otherwise */ boolean isAccountNonLocked(); /** * Indicates whether the user's credentials (password) has expired. Expired * credentials prevent authentication. * * @return <code>true</code> if the user's credentials are valid (ie non-expired), * <code>false</code> if no longer valid (ie expired) */ boolean isCredentialsNonExpired(); /** * Indicates whether the user is enabled or disabled. A disabled user cannot be * authenticated. * * @return <code>true</code> if the user is enabled, <code>false</code> otherwise */ boolean isEnabled(); }
MyUserDetailService:
package com.imooc.security.browser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.Component; /** * UserDetailsService是SpringSecurity的一个接口, * 只有一个方法:根据用户名获取用户详情 * ClassName: MyUserDetailService * @Description: TODO * @author lihaoyang * @date 2018年2月28日 */ @Component public class MyUserDetailService implements UserDetailsService{ private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private PasswordEncoder passwordEncoder; /** * UserDetails接口,实际可以自己实现这个接口,返回自己的实现类 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.info("登录用户名:"+username); //根据用户名查询用户信息 //User:springsecurity 对 UserDetails的一个实现 //为了演示在这里用passwordEncoder加密一下密码,实际中在注册时就加密,此处直接拿出密码 String password = passwordEncoder.encode("123456"); System.err.println("加密后密码: "+password); //参数:用户名|密码|是否启用|账户是否过期|密码是否过期|账户是否锁定|权限集合 return new User(username,password,true,true,true,true,AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }
在BrowserSecurityConfig里配置PasswordEncoder,用来加密密码的一个接口:
@Configuration //这是一个配置 public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{ //注意是org.springframework.security.crypto.password.PasswordEncoder @Bean public PasswordEncoder passwordencoder(){ //BCryptPasswordEncoder implements PasswordEncoder return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { //实现需要认证的接口跳转表单登录,安全=认证+授权 //http.httpBasic() //这个就是默认的弹框认证 http.formLogin() //表单认证 .and() .authorizeRequests() //下边的都是授权的配置 .anyRequest() //任何请求 .authenticated(); //都需要身份认证 } }
PasswordEncoder代码:一个加密方法,一个匹配方法,注意是org.springframework.security.crypto.password包里的接口,这个接口的实现类会给加密的密码随机加盐,所以一样的密码每次加密出来是不一样的,更安全。如123456加密2次:
加密后密码: $2a$10$BChH.C4.X8MYuI1mHFoOkefWhOsad7SvhZedHFt1OG4vjSu.z9weC
加密后密码: $2a$10$YUbz.miE5C0aAcuU1FnHSu/U.Qm/BujTNw6X7S5i4/6AhjyDc6suK
package org.springframework.security.crypto.password; /** * Service interface for encoding passwords. * * The preferred implementation is {@code BCryptPasswordEncoder}. * * @author Keith Donald */ public interface PasswordEncoder { /** * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or * greater hash combined with an 8-byte or greater randomly generated salt. */ String encode(CharSequence rawPassword); /** * Verify the encoded password obtained from storage matches the submitted raw * password after it too is encoded. Returns true if the passwords match, false if * they do not. The stored password itself is never decoded. * * @param rawPassword the raw password to encode and match * @param encodedPassword the encoded password from storage to compare with * @return true if the raw password, after encoding, matches the encoded password from * storage */ boolean matches(CharSequence rawPassword, String encodedPassword); }
访问:http://localhost:8080/user
输入正确的密码123456:可以访问user查询服务
完整代码GitHub:https://github.com/lhy1234/spring-security