实现步骤:
1.认证授权数据库中的五张表并使用mybatisplus一一映射(实现省略)。
2.使用代码生成器生成生成对应的dao层、service层、impl、domain(实现省略)。
3.实现userDetails接口;SpringSecurity用户的实体;重写方法(实现省略)。
4.生成token的工具类。
token方法参考
package com.hwqh.huawenstockuser.utils.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import springfox.documentation.builders.BuilderDefaults;
import javax.xml.bind.DatatypeConverter;
import java.util.Date;
public class JwtUtils {
private static final String secretKey = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; //进行数字签名的私钥,一定要保管好,不能和我一样写到博客中。。。。。
/**
* 一个JWT实际上就是一个字符串,它由三部分组成,头部(Header)、载荷(Payload)与签名(Signature)
*/
public static void main(String[] args) throws Exception {
//获取系统的当前时间
/* long ttlMillis = System.currentTimeMillis();
Date now = new Date(ttlMillis);*/
//生成jwt令牌
/* JwtBuilder jwtBuilder = Jwts.builder()
.setId("66")//设置jwt编码
.setSubject("程序员")//设置jwt主题
.setIssuedAt(new Date())//设置jwt签发日期
//.setExpiration(date)//设置jwt的过期时间
.claim("t", "admin")
.claim("company", "itheima")
.signWith(SignatureAlgorithm.HS256, secretKey);
//生成jwt
String jwtToken = jwtBuilder.compact();
System.out.println(jwtToken);*/
// String s = acquireJWT("123456", "1", "123");
/* byte[] bytes = DatatypeConverter.parseBase64Binary(secretKey);
//解析jwt,得到其内部的数据
Claims claims = Jwts.parser().setSigningKey(bytes).parseClaimsJws(s).getBody();
System.out.println(claims);
Claims claims1 = parseJWT(s);
System.out.println(claims1);*/
}
/**
*生成串
* @param token
* @param id
* @param account
* @return
* @throws Exception
*/
public static String acquireJWT(String token,String id,String account,Integer uid) {
//生成jwt令牌
JwtBuilder jwtBuilder = Jwts.builder()
.setId(id)//设置jwt
.setSubject("测试")//设置jwt主题
.setIssuedAt(new Date())//设置jwt签发日期
//.setExpiration(date)//设置jwt的过期时间
.claim("t", token)
.claim("account", account)
.claim("uid",uid)
// .claim("company", "itheima")
.signWith(SignatureAlgorithm.HS256, secretKey);
return jwtBuilder.compact();
}
/**
* 解析JWT字符串
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
}
5、jwt配置类。
配置类也可生成token
6、统一返回结果result(实现省略)
7、各种登录登出返回结果处理 (实现省略)。
就是实现AuthenticationSuccessHandler(登录成功) ,AuthenticationFailureHandler(登陆失败) ,LogoutSuccessHandler(登出)
8、其中登陆成功要进行token的生成 (调用工具类生成一下token)。
9、实现userDetailsService接口。根据用户名查询详细的用户信息。
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysPermissionService sysPermissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username == null || "".equals(username)) {
throw new RuntimeException("用户不能为空");
}
//根据用户名查询用户
SysUser sysUser = sysUserService.selectByName(username);
if (sysUser == null) {
throw new RuntimeException("用户不存在");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
if (sysUser != null) {
//获取该用户所拥有的权限
List<SysPermission> sysPermissions = sysPermissionService.selectListByUser(sysUser.getId());
// 声明用户授权
sysPermissions.forEach(sysPermission -> {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(sysPermission.getPermissionCode());
grantedAuthorities.add(grantedAuthority);
});
}
return new User(sysUser.getAccount(), sysUser.getPassword(), sysUser.getEnabled(), sysUser.getAccountNonExpired(), sysUser.getCredentialsNonExpired(), sysUser.getAccountNonLocked(), grantedAuthorities);
}
}
10、编写自定义登录验证。(springsecurity的核心业务处理功能) UserAuthenticationProvider implements AuthenticationProvider
最后返回:return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
package com.yaomy.security.oauth2.provider;
import com.yaomy.security.oauth2.event.event.UserLoginFailedEvent;
import com.yaomy.security.oauth2.exception.PasswordException;
import com.yaomy.security.oauth2.exception.UsernameException;
import com.yaomy.security.oauth2.service.UserAuthDetailsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* @Description: 用户自定义身份认证
* @ProjectName: spring-parent
* @Package: com.yaomy.security.provider.MyAuthenticationProvider
* @Date: 2019/7/2 17:17
* @Version: 1.0
*/
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserAuthDetailsService authUserDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private ApplicationEventPublisher publisher;
/**
* @Description 认证处理,返回一个Authentication的实现类则代表认证成功,返回null则代表认证失败
* @Date 2019/7/5 15:19
* @Version 1.0
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
if(StringUtils.isBlank(username)){
throw new UsernameNotFoundException("username用户名不可以为空");
}
if(StringUtils.isBlank(password)){
throw new BadCredentialsException("密码不可以为空");
}
//获取用户信息
UserDetails user = authUserDetailsService.loadUserByUsername(username);
//比较前端传入的密码明文和数据库中加密的密码是否相等
if (!passwordEncoder.matches(password, user.getPassword())) {
//发布密码不正确事件
publisher.publishEvent(new UserLoginFailedEvent(authentication));
throw new BadCredentialsException("password密码不正确");
}
//获取用户权限信息
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
/**
* @Description 如果该AuthenticationProvider支持传入的Authentication对象,则返回true
* @Date 2019/7/5 15:18
* @Version 1.0
*/
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
11、编写自定义权限注解验证(实现PermissionEvaluator接口)利用注解控制访问权限
参考链接
12、编写JWT接口请求校验拦截器(extends BasicAuthenticationFilter);
带token的请求和不带token的请求,返回不同结果,带token的请求要验证token的正确性。
参考链接
13、编写最最最重要的spring-security核心配置类。
继承 WebSecurityConfigurerAdapter
重写configure(AuthenticationManagerBuilder auth) 配置自定义的登录认证逻辑。返回8中自定义登录验证的逻辑。
重写configure(HttpSecurity http) 配置 security的控制逻辑。
/**
* @Author: Hutengfei
* @Description:
* @Date Create in 2019/8/28 20:15
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//登录成功处理逻辑
@Autowired
CustomizeAuthenticationSuccessHandler authenticationSuccessHandler;
//登录失败处理逻辑
@Autowired
CustomizeAuthenticationFailureHandler authenticationFailureHandler;
//权限拒绝处理逻辑
@Autowired
CustomizeAccessDeniedHandler accessDeniedHandler;
//匿名用户访问无权限资源时的异常
@Autowired
CustomizeAuthenticationEntryPoint authenticationEntryPoint;
//会话失效(账号被挤下线)处理逻辑
@Autowired
CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy;
//登出成功处理逻辑
@Autowired
CustomizeLogoutSuccessHandler logoutSuccessHandler;
//访问决策管理器
@Autowired
CustomizeAccessDecisionManager accessDecisionManager;
//实现权限拦截
@Autowired
CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
private CustomizeAbstractSecurityInterceptor securityInterceptor;
@Bean
public UserDetailsService userDetailsService() {
//获取用户账号密码及权限信息
return new UserDetailsServiceImpl();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 设置默认的加密方式(强hash方式加密)
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//用户令牌的发放和认证
auth.userDetailsService(userDetailsService());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
http.authorizeRequests().
//antMatchers("/getUser").hasAuthority("query_user").
//antMatchers("/**").fullyAuthenticated().
withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(accessDecisionManager);//决策管理器
o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
return o;
}
}).
//登出
and().logout().
permitAll().//允许所有用户
logoutSuccessHandler(logoutSuccessHandler).//登出成功处理逻辑
deleteCookies("JSESSIONID").//登出之后删除cookie
//登入
and().formLogin().
permitAll().//允许所有用户
successHandler(authenticationSuccessHandler).//登录成功处理逻辑
failureHandler(authenticationFailureHandler).//登录失败处理逻辑
//异常处理(权限拒绝、登录失效等)
and().exceptionHandling().
accessDeniedHandler(accessDeniedHandler).//权限拒绝处理逻辑
authenticationEntryPoint(authenticationEntryPoint).//匿名用户访问无权限资源时的异常处理
//会话管理
and().sessionManagement().
maximumSessions(1).//同一账号同时登录最大用户数
expiredSessionStrategy(sessionInformationExpiredStrategy);//会话失效(账号被挤下线)处理逻辑
http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);
}
}
springsercity实现登录验证参考链接(实现的方法多种多样,有使用sessionID鉴权的,有使用cookie存token鉴权的等等,根据业务自行选择,关键类WebSecurityConfig extends WebSecurityConfigurerAdapter这个最重要,其他的配置可以自定义。)
认证授权参考2