源码地址
业务需求
1.Springboot +Spring Security权限框架
2.前后端分离返回json数据,而不有后端重定向
3.可使用钉钉登陆(钉钉app企业应用)+账号密码登陆
spring security配置
1.新建WebSecurityConfig配置文件继承WebSecurityConfigurerAdapter,并重写configure的入参为HttpSecurity的方法
http.cors(withDefaults())
.csrf().disable()
.authorizeRequests(expressionInterceptUrlRegistry ->
expressionInterceptUrlRegistry
.anyRequest().authenticated()
)
.formLogin().disable()
.exceptionHandling().
authenticationEntryPoint(
(request, response, ex) -> {
SpringUtil.writeResponse(response, HttpServletResponse.SC_UNAUTHORIZED, ResultEnum.NO_LOGIN
, new HashMap<>(1), "");
}
);
2.添加自定义继承自AbstractAuthenticationProcessingFilter的Filter
public class DingTalkAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
protected DingTalkAuthenticationFilter(String defaultFilterProcessesUrl) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl, "POST"));
}
protected DingTalkAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
String code = httpServletRequest.getParameter("code");
//新建一个AuthenticationToken以便接下来的认证
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(null, code);
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(httpServletRequest));
return this.getAuthenticationManager().authenticate(authRequest);
}
3.钉钉认证DingTalkAuthenticationProvider
public class DingTalkAuthenticationProvider implements AuthenticationProvider {
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String token = authentication.getCredentials().toString();
OapiUserGetuserinfoResponse userInfo;
try{
userInfo = dingTalkUtil.getUserInfo(token);
}catch (Exception e){
throw new AuthenticationServiceException(e.getMessage());
}
String userid = userInfo.getUserid();
SysUser sysUser = sysUserService.findSysUserByToken(userid);
if (null == sysUser) {
throw new UsernameNotFoundException("钉钉code未绑定:" + userid + ",无法完成认证,请联系管理人员");
}
SysUserDetails sysUserDetails = new SysUserDetails();
BeanUtils.copyProperties(sysUser, sysUserDetails);
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(sysUserDetails, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(sysUserDetails.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(PreAuthenticatedAuthenticationToken.class);
}
4.添加filter到configure方法的HttpSecurity中
DingTalkAuthenticationFilter filter = new DingTalkAuthenticationFilter("/sys-user/dt-login");
ProviderManager providerManager =
new ProviderManager(Collections.singletonList(dingTalkAuthenticationProvider));
filter.setAuthenticationManager(providerManager);
filter.setAuthenticationFailureHandler((request, response, ex) -> {
SpringUtil.writeResponse(response, HttpServletResponse.SC_OK, ResultEnum.LOGIN_ERROR
, new HashMap<>(1), ex.getMessage());
});
filter.setAuthenticationSuccessHandler((request, response, ex) -> {
SysUser sysUser = SecurityUtil.getSysUser();
HashMap<Object, Object> result = new HashMap<>(3);
result.put("username", sysUser.getUsername());
result.put("admin", sysUser.getAdmin());
result.put("department_group_nos", Arrays.asList(sysUser.getDepartmentGroupNo()));
SpringUtil.writeResponse(response, HttpServletResponse.SC_OK, ResultEnum.SUCCESS
, result, "");
});
http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class);
6.用户名密码认证同理
public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public LoginAuthenticationFilter(String url) {
super(new AntPathRequestMatcher("/sys-user/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
LoginAuthenticationFilter filter1 = new LoginAuthenticationFilter("/sys-user/login");
ProviderManager providerManager1 =
new ProviderManager(Collections.singletonList(authenticationProvider()));
filter1.setAuthenticationManager(providerManager1);
filter1.setAuthenticationFailureHandler((request, response, ex) -> {
SpringUtil.writeResponse(response, HttpServletResponse.SC_OK, ResultEnum.LOGIN_ERROR
, new HashMap<>(1), ex.getMessage());
});
filter1.setAuthenticationSuccessHandler((request, response, ex) -> {
SysUser sysUser = SecurityUtil.getSysUser();
HashMap<Object, Object> result = new HashMap<>(3);
result.put("username", sysUser.getUsername());
result.put("admin", sysUser.getAdmin());
result.put("department_group_nos", Arrays.asList(sysUser.getDepartmentGroupNo()));
SpringUtil.writeResponse(response, HttpServletResponse.SC_OK, ResultEnum.SUCCESS
, result, "");
});
http.addFilterAt(filter1, UsernamePasswordAuthenticationFilter.class);
文章只贴出了主要代码,部分业务逻辑需要自己实现,后期将整理代码,并逐一分析实现的思想