spring security 整合sso全记录
介绍一下我司的sso流程
我司的sso流程:
- app在sso系统注册想被sso拦截的sso url, 本app注册了app/index
- sso会在公司网关层拦截 app/index的url,根据用户cookie是否携带正确token,决定是否触发登录流程(如果已经登录,则直接放行到(app/index),如果没有登录,则转发到全司统一的sso登录页面)
- sso验证通过后,会重定向到app(app/index),app可以拿到用户登录信息
app security 整合sso的思路
- app对用户提交的请求路径,利用spring security 判断用户是否登录,如未登录,则重定向到app/index, sso会在网关层抓到这个url,从而触发sso登录流程.
- 登录成功后,app拿到登录信息(userid等),设置认证通过,获取user在本app的角色等信息,并保存到security.
- 登录成功后的用户,再次访问app时,app就可以利用spring security判断用户是否登录了.
要解决的问题
- 可以看出,本案例中其实不需要spring security默认提供的登录页面,我们要让security知道我们想要跳转的登录url是app/index(POST方法)
- sso登录成功后,会跳转回到我们的app url(app/index,GET方法).怎么让security知道,并启动spring security的认证流程?
- 重写security的验证方法attemptAuthentication, 我们认为通过sso登录成功后,就默认登录app是验证通过的.
- 由于app是采用的前台loadbalance+后台机器集群的方式.前台的loadballance方式,由于我司流程,不能设置sticky session. 所以需采用springsession(redis)的方式
上代码
- 解决第一个问题,很简单,在spring-security.xml里面配置
<security:form-login login-page="/app/index"/>
- 第二个问题的解决稍微复杂一点:
我们要重写验证流程,是显而易见的.于是,新建cMToolAuthenticationFilter extends UsernamePasswordAuthenticationFilter
xml配置如下
<security:http auto-config="true" use-expressions="true">
......
<security:custom-filter ref="cMToolAuthenticationFilter" before="FORM_LOGIN_FILTER"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="cMToolAuthenticationProvider"/>
</security:authentication-manager>
<bean id="cMToolAuthenticationProvider" class="com.app.provider.CMToolAuthenticationProvider"/>
<bean id="cMToolAuthenticationFilter" class="com.app.filter.CMToolAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
在CMToolAuthenticationFilter里面,增加initCMAuFilter方法,指定触发security验证流程的url:/app/index
public class CMToolAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@PostConstruct
public void initCMAuFilter(){
//这里指定触发security验证流程的url:/app/index
RequestMatcher requiresAuthenticationRequestMatcher = new AntPathRequestMatcher("/app/index", "GET");
this.setRequiresAuthenticationRequestMatcher(requiresAuthenticationRequestMatcher);
//这里可以顺便指定一下,登录成功后的自定义handler=>CMToolSuccessHandler
this.setAuthenticationSuccessHandler(new CMToolSuccessHandler("/cmtv1web/home",userAuthenticateService));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
//写你自己的登录验证逻辑
.....
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, "");
setDetails(request, authRequest);
return super.getAuthenticationManager().authenticate(authRequest);
}
//指定authenticationManager
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
CMToolAuthenticationProvider
@Autowired
UserAuthenticateService userAuthenticateService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
//写自己的一些给用户加角色的逻辑
UserBean userBean = userAuthenticateService.getUser(username);
if(userBean!=null) {
//将user的角色交给spring security备用
List<RoleBean> roles = userBean.getRoles();
if(roles!=null&&roles.size()>0){
for(RoleBean role:roles){
authorities.add(new SimpleGrantedAuthority(role.getRole_name()));
}
}
}
return new UsernamePasswordAuthenticationToken(username, "", authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.isAssignableFrom(UsernamePasswordAuthenticationToken.class);
}
spring session redis的整合, 有时间再写吧!