springboot整合security+cas单点登陆

来源:互联网 发布:万能五笔 for mac os 编辑:程序博客网 时间:2024/05/19 22:52

参考:http://blog.csdn.net/cl_andywin/article/details/53998986

创建application.properties文件,加入以下内容:

 #CAS服务地址cas.server.host.url=http://cas.XXXX.net/cas#CAS服务登录地址cas.server.host.login_url=${cas.server.host.url}/login#CAS服务登出地址cas.server.host.logout_url=${cas.server.host.url}/logout?service=${app.server.host.url}#应用访问地址app.server.host.url=http://localhost:8080#应用登录地址app.login.url=/admin/index.html#应用登出地址app.logout.url=/logout#应用服务名称app.server.name=http://localhost:8080

security配置文件SecurityConfig,项目启动的时候会执行,初始化security和cas的设置

/** * @author mu.shuntao * @create 2017-04-10 10:44 */@Configuration@EnableWebSecurity //禁用Boot的默认Security配置,配合@Configuration启用自定义配置(需要扩展WebSecurityConfigurerAdapter)@EnableGlobalMethodSecurity(prePostEnabled = true) //启用Security注解,例如最常用的@PreAuthorizepublic class SecurityConfig extends WebSecurityConfigurerAdapter {  @Autowired  private CasProperties casProperties;  /**   * configure(AuthenticationManagerBuilder): 身份验证配置,用于注入自定义身份验证Bean和密码校验规则   * @param auth   * @throws Exception   */  @Override  protected void configure(AuthenticationManagerBuilder auth) throws Exception {    super.configure(auth);    auth.authenticationProvider(casAuthenticationProvider());  }  /**   * configure(WebSecurity): Web层面的配置,一般用来配置无需安全检查的路径   * @param web   * @throws Exception   */  @Override  public void configure(WebSecurity web) throws Exception {    web.ignoring().antMatchers("/static/**", "/templates/**");  }  /**   * configure(HttpSecurity): Request层面的配置,对应XML Configuration中的<http>元素   * @param http   * @throws Exception   */  @Override  protected void configure(HttpSecurity http) throws Exception {    http.authorizeRequests()//配置安全策略      .antMatchers("/").permitAll()//所有请求都不需要验证      //.antMatchers("/admin/**").authenticated()//admin下请求需要验证      .and()      .logout()      .permitAll()//定义logout不需要验证      .and()      .formLogin();    http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())      .and()      .addFilter(casAuthenticationFilter())      .addFilterBefore(casLogoutFilter(), LogoutFilter.class)      .addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class);    http.csrf().disable();    // 关闭spring security默认的frame访问限制    http.headers().frameOptions().sameOrigin();  }  @Bean  public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {    CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();    casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());    casAuthenticationEntryPoint.setServiceProperties(serviceProperties());    return casAuthenticationEntryPoint;  }  /**   * 指定service相关信息   */  @Bean  public ServiceProperties serviceProperties() {    ServiceProperties serviceProperties = new ServiceProperties();    serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());    serviceProperties.setAuthenticateAllArtifacts(true);    return serviceProperties;  }  /**   * CAS认证过滤器   */  @Bean  public CasAuthenticationFilter casAuthenticationFilter() throws Exception {    CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();    casAuthenticationFilter.setAuthenticationManager(authenticationManager());    casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl());    return casAuthenticationFilter;  }  /**   * cas 认证 Provider   */  @Bean  public CasAuthenticationProvider casAuthenticationProvider() {    CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();    casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());    //casAuthenticationProvider.setUserDetailsService(customUserDetailsService()); //这里只是接口类型,实现的接口不一样,都可以的。    casAuthenticationProvider.setServiceProperties(serviceProperties());    casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());    casAuthenticationProvider.setKey("casAuthenticationProviderKey");    return casAuthenticationProvider;  }   /* @Bean    public UserDetailsService customUserDetailsService(){        return new CustomUserDetailsService();    }*/  //用户自定义的AuthenticationUserDetailsService  @Bean  public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() {    return new CustomUserDetailsService();  }  @Bean  public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {    return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl());  }  /**   * 单点登出过滤器   */  @Bean  public SingleSignOutFilter singleSignOutFilter() {    SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();    singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());    singleSignOutFilter.setIgnoreInitConfiguration(true);    return singleSignOutFilter;  }  /**   * 请求单点退出过滤器   */  @Bean  public LogoutFilter casLogoutFilter() {    LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(), new SecurityContextLogoutHandler());    logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl());    return logoutFilter;  }}

这里在指定service里设置的是,就是http://localhost:8080/admin/index.html路径,保证这个路径能正常返回,登陆成功后才能正常返回ticket,执行后面的CustomUserDetailsService类中的loadUserDetails方法
serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());

User类,用于封装登陆成功后的用户信息,登陆成功后程序里获取用户信息可以使用

User userDetails = (User) SecurityContextHolder.getContext()              .getAuthentication()              .getPrincipal();      String username = userDetails.getUsername();      String id = userDetails.getId();

页面获取用户信息可以使用,如下为获取用户名

${Session.SPRING_SECURITY_CONTEXT.authentication.principal.username}
/** * @author mu.shuntao * @create 2017-04-10 13:59 */public class User implements UserDetails{  /**   * 用户ID   */  private String id;  /**   * 用户名称   */  private String name;  /**   * 登录名称   */  private String username;  /**   * 登录密码   */  private String password;  private boolean isAccountNonExpired = true;  //是否过期  private boolean isAccountNonLocked = true; //账户未锁定为true  private boolean isCredentialsNonExpired = true; //证书不过期为true  private boolean isEnabled = true; //是否可用  private Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();  @Override  public Collection<? extends GrantedAuthority> getAuthorities() {      return authorities;  }  @Override  public String getPassword() {      return password;  }  @Override  public String getUsername() {      return username;  }  @Override  public boolean isAccountNonExpired() {      return isAccountNonExpired;  }  @Override  public boolean isAccountNonLocked() {      return isAccountNonLocked;  }  @Override  public boolean isCredentialsNonExpired() {      return isCredentialsNonExpired;  }  @Override  public boolean isEnabled() {      return isEnabled;  }  public String getId() {      return id;  }  public void setId(String id) {      this.id = id;  }  public String getName() {      return name;  }  public void setName(String name) {      this.name = name;  }  public void setUsername(String username) {      this.username = username;  }  public void setPassword(String password) {      this.password = password;  }  public void setAccountNonExpired(boolean accountNonExpired) {      isAccountNonExpired = accountNonExpired;  }  public void setAccountNonLocked(boolean accountNonLocked) {      isAccountNonLocked = accountNonLocked;  }  public void setCredentialsNonExpired(boolean credentialsNonExpired) {      isCredentialsNonExpired = credentialsNonExpired;  }  public void setEnabled(boolean enabled) {      isEnabled = enabled;  }}

CustomUserDetailsService 类,cas服务登陆成功后会自动执行loadUserDetails方法,我的cas服务返回了所有用户信息,直接从json里取出用户信息封装到User里。如果cas服务只返回用户id,可以拿着id去数据库查用户信息。

/** * @author mu.shuntao * @create 2017-04-10 13:54 */public class CustomUserDetailsService  implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {    @Override    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {        User user = new User();        Map<String, Object> userAttributess = token.getAssertion().getPrincipal().getAttributes();        if (userAttributess != null) {            String userInfoJson = String.valueOf(userAttributess.get("userInfo"));            if (StringUtils.isNotEmpty(userInfoJson)) {                JSONObject userInfo = JSONObject.parseObject(userInfoJson);                String userAppJson = String.valueOf(userAttributess.get("userApp"));                JSONArray jsonArray = JSONObject.parseArray(userAppJson);                if (jsonArray != null && jsonArray.size() > 0) {                    boolean canLogin = false;                    for (int i = 0; i < jsonArray.size(); i++) {                        JSONObject appInfo = jsonArray.getJSONObject(i);                        //判断用户是否有该应用登陆权限                        if (appInfo.get("appId").equals("abc")) {                            canLogin = true;                            break;                        }                    }                    // 如果可以登录系统则返回用户数据                    if (canLogin) {                        user.setUsername(userInfo.getString("userName"));                        user.setName(userInfo.getString("userRealName"));                        user.setPassword(userInfo.getString("password"));                        user.setId(userInfo.getString("userId"));                    }                }            }        }        return user;    }}

最后附上cas服务端给客服端返回自定义数据的方法,自定义一个类UserInfoPrincipalResolver ,实现PrincipalResolver 接口,主要是复写resolve方法
,在resolve方法里封装数据

public class UserInfoPrincipalResolver implements PrincipalResolver {    private UserService userService;    public void setUserService(UserService userService) {        this.userService = userService;    }    /**     * Factory to create the principal type.     **/    @NotNull    protected PrincipalFactory principalFactory = new DefaultPrincipalFactory();    private PasswordEncoder passwordEncoder = new PlainTextPasswordEncoder();    /**     * Optional principal attribute name.     */    protected String principalAttributeName;    @Override    public boolean supports(final Credential credential) {        return true;    }    @Override    public Principal resolve(final Credential credential) {        UsernamePasswordCredential captchaCredential = (UsernamePasswordCredential) credential;        final String principalId = extractPrincipalId(credential);        if (principalId == null) {            return null;        }        final Pair<String, Map<String, Object>> pair = convertPersonAttributesToPrincipal(principalId, captchaCredential);        return this.principalFactory.createPrincipal(pair.getFirst(), pair.getSecond());    }    /**     * 设置传递给客户端的数据     *     * @param extractedPrincipalId     * @return     */    protected Pair<String, Map<String, Object>> convertPersonAttributesToPrincipal(final String extractedPrincipalId, UsernamePasswordCredential credential) {        final Map<String, Object> convertedAttributes = new HashMap<>();        String principalId = extractedPrincipalId;        String username = credential.getUsername();        String encryptedPassword = this.getPasswordEncoder().encode(credential.getPassword());        if ("MD5".equals(credential.getPasswordFlag())) {            encryptedPassword = credential.getPassword();        }        UserEntity userEntityParam = new UserEntity();        userEntityParam.setUserName(username);        userEntityParam.setPassword(encryptedPassword);        UserEntity userEntity = this.userService.checkUserLogin(userEntityParam);        userEntity = this.userService.getUserInfo(userEntity.getUserId());        UserAppEntity userAppEntity = new UserAppEntity();        userAppEntity.setUserId(userEntity.getUserId());        List<UserAppEntity> userAppEntityList = this.userService.findUserAppList(userAppEntity);        convertedAttributes.put("userInfo", JSONObject.toJSONString(userEntity));        convertedAttributes.put("userApp", JSONObject.toJSONString(userAppEntityList));        return new Pair<>(principalId, convertedAttributes);    }    public void setPrincipalFactory(final PrincipalFactory principalFactory) {        this.principalFactory = principalFactory;    }    /**     * Sets the PasswordEncoder to be used with this class.     *     * @param passwordEncoder the PasswordEncoder to use when encoding     *                        passwords.     */    public void setPasswordEncoder(final PasswordEncoder passwordEncoder) {        this.passwordEncoder = passwordEncoder;    }    /**     * Method to return the PasswordEncoder to be used to encode passwords.     *     * @return the PasswordEncoder associated with this class.     */    private PasswordEncoder getPasswordEncoder() {        return this.passwordEncoder;    }    protected String extractPrincipalId(final Credential credential) {        return credential.getId();    }}
0 0
原创粉丝点击